Files
ipam_control/docs/changes/011-address-unique-in-vrf/PLAN.md
T
ayurishchevandClaude Opus 5.5 13e17fbb47 Задачи 011-024: доработки по ревью кодовой базы и исправление находок
Ревью кодовой базы (docs/reviews/2026-09-26-codebase-review.md) и планы по каждой находке:
011 IP уникален в VRF и хранится в самом узком префиксе (addresses.vrf_id, составной FK
    с каскадом при переносе VRF, миграция 0007 с остановкой на дублях).
012 Ограничение попыток входа (login_attempts, 429 + Retry-After), выравнивание времени
    ответа, журнал без вытеснения анонимными событиями (миграция 0006).
013 Границы пагинации: отрицательные/чрезмерные limit/offset дают 422 вместо 500.
014 Экран адресов: страница свободных адресов арифметикой, пагинация в SQL.
015 Запрет адреса сети/broadcast, загрузка не выше 100 %.
016 Роль по умолчанию — viewer.
017 Проверка JWT_SECRET/ADMIN_PASSWORD при старте.
018 null в PATCH очищает текстовые поля; нейтральный текст конфликта БД.
019 Пакетная загрузка в списках вместо N+1.
020 Автоназначение адреса вне вложенных префиксов, с блокировкой префикса.
021 Advisory-lock при снятии прав администратора, уникальный lower(username) (миграция 0008).
022 Контейнер не от root, healthcheck, блокировка миграций, requirements.lock.
023 Экранирование LIKE, журнал отказов очистки, заголовки безопасности, учёт force-удаления,
    отзыв токенов при смене пароля (claim pv, миграция 0005).
024 Исправление находок ревью 011-023 (docs/reviews/2026-09-26-changes-011-023-review.md):
    сериализация попыток входа, запрет переноса адресов в адрес сети/broadcast, журнал входов,
    запрет смены своего пароля через PATCH, валидация PATCH устройства, обновлён тест токенов.

Тесты: 14 passed. Документация: README.md, docs/changes/011-024, docs/reviews.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:33:50 +03:00

37 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Уникальность IP-адреса в VRF (изменение 011)
Находка ревью № 1 (`docs/reviews/2026-09-26-codebase-review.md`), серьёзность — высокая.
## Context
Один и тот же IP можно завести и в родительском, и в дочернем префиксе одного VRF: уникальна только пара `(prefix_id, address)`.
Воспроизведено: `198.51.100.5` создаётся в `/24` и во вложенном `/25` (оба 201), у родителя `used` завышен. Нужен инвариант:
**адрес хранится только в самом узком префиксе своего VRF и уникален в VRF**, причём на уровне БД.
## Решение
1. **Схема** (новая миграция Alembic):
- `prefixes`: уникальное ограничение `(id, vrf_id)` — цель составного FK;
- `addresses`: колонка `vrf_id` (NOT NULL, заполняется из `prefixes`), составной FK `(prefix_id, vrf_id) → prefixes(id, vrf_id)` **ON UPDATE CASCADE**
(перенос префикса в другой VRF сам обновит адреса), уникальный индекс `(vrf_id, address)`.
- Перед созданием индекса миграция ищет дубли и при их наличии **останавливается** с перечнем `VRF · адрес · префиксы` (данные не удаляются автоматически).
Для разбора — скрипт `scripts/find_duplicate_addresses.py` (только чтение).
2. **API** (`app/api/v1/prefixes.py`):
- `create_address`: если адрес попадает в дочерний префикс того же VRF — 422 «Адрес принадлежит вложенному префиксу X, назначьте его там»; дубль в VRF — 409 (через `flush`).
- `attach_to_tree` (создание префикса, `allocate_subnet`, перенос VRF): адреса родителя из диапазона нового префикса переносятся в него
(`UPDATE addresses SET prefix_id = :new WHERE prefix_id = :parent AND address <<= :cidr`); число перенесённых — в `diff` записи `prefix.created` (`moved_addresses`).
- `_move_to_vrf`: конфликт адресов в целевом VRF — 409 с перечнем (как для префиксов), без частичных изменений.
3. **Модель** `Address`: `vrf_id` + ограничения из п. 1; `Address(...)` заполняет `vrf_id` из префикса.
4. `_usage` не меняется: после инварианта двойного счёта нет.
## Файлы
`alembic/versions/<next>_address_vrf_unique.py`, `app/models.py`, `app/api/v1/prefixes.py`, `scripts/find_duplicate_addresses.py`, `tests/test_api.py`, `README.md` (Модель данных).
## Тест (один сценарий)
Родитель `/24` с адресом `.5`, создание дочернего `/25` → адрес переехал в дочерний; повторный `.5` в родителе → 422; `.5` в дочернем → 409; перенос дочернего в VRF с `.5` → 409.
## Проверка
- Миграция на копии данных стенда `ipam_control_006`: без дублей — проходит; с искусственным дублем — останавливается с понятным сообщением.
- `pytest -q`; вручную в UI: «Назначить адрес» в родителе на адрес из дочернего — понятная ошибка.
## Риски
Изменение модели данных; на рабочей БД до миграции запустить скрипт поиска дублей. Зависимость: № 020 (автоназначение) опирается на этот инвариант.