Backend (FastAPI, SQLAlchemy 2, Alembic, PostgreSQL 16): - организации, VRF, префиксы (дерево, использование, автоназначение), адреса, операторы связи, устройства и типы устройств; JWT, роли admin/viewer; - VRF принадлежит организации (составной FK), смена VRF у префикса переносит поддерево, имя VRF уникально в организации; - журнал аудита: поиск и фильтры, ротация (срок/количество), очистка по паролю с блокировкой, IP клиента и метаданные запроса (X-Forwarded-For только от TRUSTED_PROXIES). UI (web/, без сборки): экраны и диалоги по макетам «IPAM Manager», кликабельные строки реестров, локальные шрифты IBM Plex, собственные выпадающие списки. Окружение: docker-compose (postgres + app), миграции Alembic 0001-0004, scripts/gen_env.py, scripts/seed_demo.py, 11 автотестов (pytest). Документация: README.md и docs/changes/001-005 (планы и итоги). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
52 lines
6.1 KiB
Markdown
52 lines
6.1 KiB
Markdown
# План: смена VRF у префикса и целостность «VRF ⊂ организация» (изменение 002)
|
|
|
|
## Контекст
|
|
VRF — часть адресного плана организации. Сейчас:
|
|
- уникальность VRF уже в пределах организации: `UNIQUE(organization_id, name)` — одно имя допустимо в разных организациях;
|
|
- несколько префиксов организации могут быть в одном VRF (`UNIQUE(vrf_id, prefix)` — дубль CIDR запрещён только внутри VRF);
|
|
- **смена VRF у существующего префикса невозможна**: нет ни поля в UI (`web/app.js`, `prefixDialog`), ни `vrf_id` в `PrefixUpdate` (`app/schemas.py`);
|
|
- принадлежность VRF организации префикса проверяется только в коде (`create_prefix`), в БД гарантии нет.
|
|
|
|
Цель: разрешить смену VRF **только среди VRF той же организации**, сохранив целостность дерева, и закрепить правило на уровне БД.
|
|
|
|
## Правила
|
|
1. Организация префикса неизменна; целевой VRF обязан принадлежать той же организации (иначе 422).
|
|
2. Смена VRF переносит **префикс вместе со всем поддеревом** (вложенные по `parent_id`), чтобы дерево не «разрывалось» между VRF.
|
|
3. Дубль `(vrf, prefix)` у любого переносимого префикса в целевом VRF → 409 с перечнем конфликтующих CIDR; перенос отменяется целиком.
|
|
4. После переноса родитель корня поддерева пересчитывается в целевом VRF (самый узкий объемлющий); префиксы целевого VRF, лежащие внутри перенесённого, переподчиняются ему — та же логика, что при создании (выносим в общую функцию `attach_to_tree`).
|
|
5. Адреса остаются у своих префиксов (`prefix_id` не меняется); использование/ёмкость пересчитываются автоматически.
|
|
6. CIDR и родитель в окне редактирования по-прежнему не меняются (родитель определяется автоматически).
|
|
|
|
## Изменения
|
|
**Backend**
|
|
- `app/schemas.py`: `PrefixUpdate.vrf_id: int | None`.
|
|
- `app/api/v1/prefixes.py`: в `update_prefix` — валидация правил 1–3, перенос поддерева, `attach_to_tree` (рефакторинг из `create_prefix`), запись в audit `{vrf: "old → new", moved: N}`.
|
|
- Alembic `0002`:
|
|
- `UNIQUE(id, organization_id)` на `vrfs` + составной FK `prefixes(vrf_id, organization_id) → vrfs(id, organization_id)` — БД не даст связать префикс с VRF чужой организации;
|
|
- уникальность имени VRF без учёта регистра в пределах организации: индекс `(organization_id, lower(name))` вместо `UNIQUE(organization_id, name)`; перед применением — проверка на существующие дубли.
|
|
|
|
**UI (`web/app.js`)**
|
|
- В окне редактирования префикса показать поле «VRF» (только VRF текущей организации, уже загружены в `S.vrfs`).
|
|
- При выборе другого VRF — подсказка «Вместе с префиксом будет перенесено N вложенных» (N считается из `S.prefixes`); ошибки 409/422 — в стандартном баннере окна.
|
|
- Вёрстка — из существующего окна создания (макета редактирования нет, отступлений от дизайна не появляется).
|
|
|
|
**Тесты (2 новых, всего 6)**
|
|
1. Одноимённый VRF в двух организациях — ок; повтор в одной (в т.ч. другим регистром) — 409.
|
|
2. Смена VRF: поддерево переехало, родитель пересчитан, дубль в целевом VRF — 409 без частичных изменений, VRF другой организации — 422.
|
|
|
|
## Порядок работ
|
|
1. Рефакторинг `attach_to_tree` (поведение создания не меняется, тесты зелёные).
|
|
2. Миграция `0002` (сначала проверка дублей, затем применение через `docker compose up`).
|
|
3. `PrefixUpdate` + логика переноса.
|
|
4. UI: поле VRF и подсказка.
|
|
5. Тесты в контейнерах, прогон e2e-сценария UI, сверка окна с макетом «Новый префикс».
|
|
6. Обновить `README.md` (правила VRF), написать `SUMMARY.md` в этой папке.
|
|
|
|
## Риски
|
|
- Составной FK требует, чтобы `prefixes.organization_id` всегда совпадал с организацией VRF — уже так; миграция проверит данные и откажется применяться при расхождении.
|
|
- Перенос крупного поддерева — одна транзакция; при 409 откат целиком.
|
|
|
|
## Допущения (скажите, если не так)
|
|
- Переносится всё поддерево, а не только выбранный префикс (альтернатива — запретить смену VRF у префиксов с вложенными).
|
|
- Регистронезависимая уникальность имён VRF в организации допустима (`Lab` и `lab` считаются одним VRF).
|