Control Server (FastAPI) с WEB UI (Jinja2 + HTMX) и JSON API для группового администрирования устройств RouterOS 7 через REST. - Устройства: список, статус (модель, канал, версии ROS/FW, uptime, доступные обновления), примечания, пароли шифруются (Fernet); фоновый опрос каждые 30 с и быстрое обнаружение недоступности (таймаут соединения 4 с). - Группы устройств и фильтры (устройства, резервные копии). - Резервные копии: .backup и .rsc (show-sensitive) создаются через REST, скачиваются сервером и загружаются в S3 (Yandex Object Storage). - Обновление ROS/FW и выбор канала, групповые операции задачами. - Интерфейс по утверждённому макету: светлая/тёмная темы, окна, шрифты IBM Plex локально. Docker Compose, SQLite в томе, минимальный набор тестов. - Планы и итоги каждого изменения — в docs/changes/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
136 lines
14 KiB
Markdown
136 lines
14 KiB
Markdown
# ros_control
|
||
|
||
Централизованное управление парком MikroTik RouterOS: WEB UI и JSON API.
|
||
Дизайн и требования — `ros_control.svg`; макет интерфейса — утверждён и перенесён в приложение (см. `docs/changes/006-ui-redesign`).
|
||
|
||
```
|
||
Admin Dashboard ⇄ Control Server ⇄ RouterOS REST (на каждом устройстве)
|
||
⇅ ⇅
|
||
Metadata DB S3 (Yandex Object Storage)
|
||
```
|
||
|
||
## Возможности
|
||
|
||
**Устройства**
|
||
- Список устройств: добавление, изменение, удаление; пароли хранятся зашифрованно (Fernet). Имя устройства в таблице кликабельно — открывает окно изменения.
|
||
- К устройству можно добавить **примечание** (до 500 символов): видно подсказкой при наведении на имя.
|
||
- Статус: online/offline, модель, канал обновлений, версии ROS и FW, uptime, время запроса бэкапа. Колонки **Upgrade ROS** и **Upgrade FW** показывают версию, до которой можно обновиться (устройство проверяет обновления на серверах MikroTik; без интернета на устройстве — «—»).
|
||
- **Мониторинг доступности**: сервер опрашивает устройства каждые 30 с (`POLL_INTERVAL`), недоступность определяется за ~4 с на устройство (`ROS_CONNECT_TIMEOUT`); страница обновляет статусы сама, без перезагрузки.
|
||
|
||
**Группы и фильтры**
|
||
- Устройство относится к одной группе: вкладки групп со счётчиками, страница «Группы» (создание, переименование, удаление), перенос отмеченных устройств в группу. При добавлении устройства группу можно создать «на месте» (пункт «+ Новая группа…»).
|
||
- Фильтры устройств: поиск (имя, адрес, модель), статус, наличие обновлений, канал. Фильтры бэкапов: группа, устройство, тип файла, период, имя файла. Фильтры попадают в URL.
|
||
|
||
**Операции**
|
||
- Действия по устройству — в меню «⋯» строки; действия над выбранными устройствами (бэкап, статус, обновление ROS/FW, канал, перенос в группу) появляются в полосе инструментов при выборе строк; через API их можно запускать и над всей группой.
|
||
- **Бэкап**: `.backup` (без шифрования) и `.rsc` (с `show-sensitive`, т.е. с паролями и ключами — из него можно восстановить все сущности, использующие пароль). Сервер создаёт файлы через REST, скачивает во временную папку, загружает в S3 и удаляет файлы с устройства.
|
||
- Список бэкапов в бакете, скачивание (временная ссылка), удаление.
|
||
- Канал обновлений; обновление ROS (устройство перезагружается после скачивания); обновление FW (перезагрузка сразу после появления в журнале устройства записи «Firmware upgraded successfully…»).
|
||
- Долгие и групповые операции выполняются задачами (карточка «Задачи», состояние — в UI и через API).
|
||
|
||
## Интерфейс
|
||
|
||
Экран строится сверху вниз: приложение (навигация) → страница (заголовок, счётчики, главное действие) → вкладки групп → полоса инструментов таблицы → данные; «Задачи» — отдельная карточка. Пока ничего не выбрано, полоса показывает фильтры; при выборе строк — действия над выбранными.
|
||
Добавление и изменение устройств, создание и переименование групп — в окнах поверх страницы (запасные страницы `/devices/new`, `/devices/{id}/edit` работают без JavaScript). Выпадающие меню не обрезаются таблицей и раскрываются вверх, если снизу нет места. Светлая и тёмная темы переключаются по настройке системы. Шрифты IBM Plex лежат в `app/ui/static/fonts` (лицензия OFL), внешние ресурсы не загружаются.
|
||
|
||
## Требования к устройствам
|
||
|
||
- RouterOS **7.1+**, включённый сервис `www-ssl` (REST API, порт 443; **не** `api-ssl` 8729 и не `api` 8728). Если `www-ssl` недоступен, для доверенной сети можно указать сервис `www` (порт 80) и снять флаг «HTTPS» у устройства — логин и пароль пойдут открытым текстом.
|
||
- Пользователь с политиками `read`, `write`, `policy`, `test`, `ftp`, `reboot`, `sensitive` (проще всего — группа `full`). Встроенная группа `write` с запретом `ftp`/`policy` не подходит: бэкап падает с `not enough permissions`. После смены прав пересоздайте подключение к устройству.
|
||
- Доступ устройств к S3 не нужен: файлы забирает и загружает Control Server (ему нужен доступ к `storage.yandexcloud.net:443`).
|
||
|
||
## Безопасность
|
||
|
||
- Замените значения по умолчанию `API_TOKEN` и `SESSION_SECRET` на случайные (`openssl rand -hex 24`); порт 8000 не стоит открывать в недоверенную сеть.
|
||
- Пароли устройств шифруются ключом `SECRET_KEY` (Fernet); потеря ключа = потеря доступа к сохранённым паролям.
|
||
- **Секреты в бэкапах:** `.rsc` создаётся с `show-sensitive`, а `.backup` — без шифрования, поэтому файлы содержат пароли и ключи открытым текстом. Ограничьте доступ к бакету и ссылкам скачивания.
|
||
|
||
## Запуск
|
||
|
||
```bash
|
||
cp .env.example .env # заполнить SECRET_KEY, ADMIN_PASSWORD, API_TOKEN, S3_*
|
||
docker compose up -d --build # UI: http://localhost:8000, OpenAPI: /docs
|
||
```
|
||
|
||
БД (SQLite) хранится в Docker-томе `ros_data` (`docker volume inspect ros_control_ros_data`), переживает пересборку контейнера; схема обновляется автоматически при старте. Остановка: `docker compose down` (том сохраняется; `down -v` удалит БД).
|
||
|
||
Ключ Fernet: `python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"`.
|
||
|
||
### Настройки (`.env`)
|
||
|
||
| Переменная | По умолчанию | Назначение |
|
||
|---|---|---|
|
||
| `SECRET_KEY` | — | ключ Fernet для паролей устройств (обязателен) |
|
||
| `SESSION_SECRET` | `change-me` | подпись cookie-сессии UI |
|
||
| `ADMIN_USER` / `ADMIN_PASSWORD` | `admin` / `change-me` | вход в UI |
|
||
| `API_TOKEN` | `change-me` | Bearer-токен API |
|
||
| `DATABASE_URL` | `sqlite:///./data/ros_control.db` | база метаданных (в compose — том) |
|
||
| `S3_ENDPOINT`, `S3_REGION`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_PREFIX` | Yandex Object Storage, `backups` | бакет для резервных копий |
|
||
| `MAX_CONCURRENCY` | `10` | одновременные обращения к устройствам |
|
||
| `ROS_CONNECT_TIMEOUT` | `4` | секунд на установление соединения (скорость обнаружения недоступности) |
|
||
| `ROS_TIMEOUT` | `30` | секунд на ответ устройства (долгие операции) |
|
||
| `POLL_INTERVAL` | `30` | период фонового опроса, с; `0` — выключить |
|
||
| `UPDATE_CHECK_INTERVAL` | `1800` | как часто опрос проверяет обновления ROS, с |
|
||
|
||
### Разработка
|
||
|
||
```bash
|
||
python3 -m venv venv && ./venv/bin/pip install -r requirements.txt
|
||
set -a; . ./.env; set +a
|
||
./venv/bin/uvicorn app.main:app --reload
|
||
./venv/bin/python -m pytest # 9 тестов, фоновый опрос в тестах выключен
|
||
```
|
||
|
||
## API v1
|
||
|
||
Заголовок `Authorization: Bearer <API_TOKEN>`. Интерактивная документация — `/docs`. Пример:
|
||
|
||
```bash
|
||
curl -s -H "Authorization: Bearer $API_TOKEN" http://localhost:8000/api/v1/devices | python3 -m json.tool
|
||
```
|
||
|
||
Устройство: `name`, `host`, `port`, `username`, `password` (только при записи), `verify_tls`, `use_tls` (`false` — HTTP), `group_id` (`null` — без группы), `note`.
|
||
|
||
| Метод | Путь | Назначение |
|
||
|---|---|---|
|
||
| GET/POST | `/api/v1/devices` | список (фильтры `group`, `q`, `status`, `updates`, `channel`) / добавить |
|
||
| GET/PATCH/DELETE | `/api/v1/devices/{id}` | получить / изменить / удалить |
|
||
| POST | `/api/v1/devices/refresh`, `/devices/{id}/refresh` | обновить статус |
|
||
| POST | `/api/v1/devices/{id}/backups` | бэкап (задача) |
|
||
| PUT | `/api/v1/devices/{id}/update/channel` | `{"channel": "stable\|long-term\|testing\|development"}` |
|
||
| POST | `/api/v1/devices/{id}/update/install` | обновление ROS (задача) |
|
||
| POST | `/api/v1/devices/{id}/firmware/upgrade` | обновление FW (задача) |
|
||
| GET/POST | `/api/v1/groups` | группы (с числом устройств) / создать |
|
||
| PATCH/DELETE | `/api/v1/groups/{id}` | переименовать / удалить (устройства остаются без группы) |
|
||
| POST | `/api/v1/batch/{backup\|ros_update\|fw_update}` | `{"device_ids": [...]}` и/или `{"group_id": N}` — групповая операция |
|
||
| PUT | `/api/v1/batch/channel` | `{"device_ids": [...] или "group_id": N, "channel": "..."}` |
|
||
| GET/DELETE | `/api/v1/backups`, `/backups/download?key=` | бэкапы в бакете (фильтры `device_id`, `group`, `kind`, `date_from`, `date_to`, `q`) |
|
||
| GET | `/api/v1/jobs`, `/jobs/{id}` | состояние задач |
|
||
|
||
Внешние справочники: [RouterOS REST API](https://help.mikrotik.com/docs/spaces/ROS/pages/47579162/REST+API#RESTAPI-HTTPMethods),
|
||
[Yandex Object Storage S3 API](https://yandex.cloud/ru/docs/storage/s3/api-ref/).
|
||
|
||
## Структура
|
||
|
||
- `app/ros` — клиент RouterOS REST и операции (статус, бэкап, скачивание файлов, обновления)
|
||
- `app/s3.py` — бакет; `app/security.py` — шифрование и авторизация; `app/models.py`, `app/db.py` — БД и миграции
|
||
- `app/services` — устройства и фильтры, группы, бэкапы, операции, задачи, фоновый опрос (`poller.py`)
|
||
- `app/api` — JSON API; `app/ui` — WEB UI (Jinja2 + HTMX, `static/` со стилями, скриптом и шрифтами)
|
||
- `tests` — минимальный набор; `docs/changes` — планы и итоги каждого изменения
|
||
|
||
## История изменений
|
||
|
||
Планы и итоги — в `docs/changes/`:
|
||
- `001-initial-implementation` — первая версия.
|
||
- `002-http-support-and-fixes` — HTTP-подключение, исправления по итогам теста на реальном устройстве.
|
||
- `003-download-via-rest-upload-from-server` — бэкап: скачивание файлов через REST, загрузка в S3 сервером.
|
||
- `004-upgrade-columns-and-actions-menu` — колонки Upgrade ROS/FW, меню действий «⋯».
|
||
- `005-groups-and-filters` — группы устройств, фильтры устройств и бэкапов.
|
||
- `006-ui-redesign` — новый интерфейс по утверждённому макету; исправлены перезагрузка после обновления FW и привязка группы при добавлении устройства.
|
||
- `007-fast-offline-detection` — фоновый опрос устройств (30 с), быстрый таймаут соединения, автообновление страницы.
|
||
- `008-device-note` — примечание к устройству.
|
||
- `009-dialog-label-alignment` — выравнивание полей в окне устройства.
|
||
- `010-export-show-sensitive` — `.rsc` создаётся с `show-sensitive` (секреты в файле).
|
||
- `011-dropdown-clipping` — выпадающие меню не обрезаются в узком окне.
|
||
- `012-clickable-device-name` — клик по имени устройства открывает окно изменения.
|