006 Пользователи: API /users (CRUD, смена своего пароля), раздел UI «Пользователи»,
события журнала user.*, защита от отключения/удаления себя и последнего админа.
007 Исправление удаления организации: VRF удаляются явным DELETE до организации
(без relationship() порядок DELETE не гарантирован → ложный 409).
008 Журнал фиксирует отказы в удалении (<entity>.delete_blocked) со списком
мешающих объектов в «Данных»: организация, VRF, тип устройства, префикс, пользователь.
009 Выбор строк чекбоксами и групповые операции в UI (удаление, смена типа устройств,
статус префиксов и адресов, доступ пользователей); цикл запросов из UI, итог и список отказов.
010 Автовыделение следующего вложенного префикса: POST/GET /prefixes/{id}/subnets/next,
первый свободный выровненный блок; пункт «Добавить вложенный (авто)» в меню префикса.
Тесты: 14 (добавлены сценарии для 006, 007/008, 010); исправлена нестабильность
тестов журнала (IPv6-группы с ведущими нулями нормализуются PostgreSQL).
Документация: README.md, docs/changes/006-010 (планы и итоги).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6.4 KiB
6.4 KiB
Автовыделение следующего вложенного префикса (изменение 010)
Context
Когда префикс-контейнер (например 172.20.0.0/24) дробят на подсети одного размера (/30), свободный блок приходится считать вручную.
Нужна кнопка: пользователь выбирает родителя и размер (/30), система сама находит первый свободный выровненный блок внутри родителя и создаёт дочерний префикс.
Алгоритм выбора блока (app/services.py, чистая функция, без БД)
next_free_subnet(parent_cidr, length, occupied) -> str | None:
occupied— диапазоны[start, end](целые) всех уже существующих префиксов внутри родителя в том же VRF (все уровни вложенности) и адресов, записанных на самом родителе (чтобы не выдать блок, где уже есть назначенные IP). Диапазоны сортируются и склеиваются.- Кандидат начинается с адреса родителя и выравнивается по размеру блока (
ceil(start / size) * size); если пересекает занятый диапазон — перескок за его конец, повторное выравнивание. Сложность O(n) от числа занятых диапазонов, без перебора всех подсетей (работает и для/8 → /30, и для IPv6). - Кандидат не выходит за границу родителя; иначе
None. Первый подходящий (наименьший адрес) — предсказуемо:.0/30,.4/30,.8/30…; для смешанных размеров блок выравнивается сам (/29после.0/30,.4/30→.8/29).
API (app/api/v1/prefixes.py, схемы в app/schemas.py)
POST /prefixes/{id}/subnets/next(admin) — телоSubnetNextIn:length(int),description,status(по умолчаниюactive),is_pool,note. Создаёт префикс в VRF и организации родителя,parent_id = id. ВозвращаетPrefixOut(201). Ошибки:lengthне больше длины родителя или больше 32/128 → 422 (с пределами в тексте); нет свободного блока → 409 «В префиксе нет свободного блока /30». Родитель блокируетсяSELECT … FOR UPDATE— параллельные запросы не выдают один блок; уникальность(vrf_id, prefix)остаётся страховкой (flush→ 409). Переиспользуютсяget_or_404,flush/commit,attach_to_tree(keep_parent=True)(подхват существующих вложенных, если они шире),audit(... "created", …, {"vrf": …, "allocated_from": "<родитель>"}),_prefix_outs.GET /prefixes/{id}/subnets/next?length=30— предпросмотр без создания:{"prefix": "172.20.0.4/30"|null, "length_min": 25, "length_max": 32}(для окна в UI; те же проверки диапазона).- БД, миграции, существующие эндпоинты не меняются.
UI (web/app.js)
- Пункт «Добавить вложенный (авто)» в меню «⋯» строки префикса: у листовых — рядом с «Открыть адреса/Редактировать/Удалить»; у родителей (сейчас без меню) — меню из этого одного пункта. Доступно только admin (как остальные меню записи).
- Окно
subnetDialog(parent)«Новый вложенный префикс в 172.20.0.0/24»: выпадающий «Размер» (/25 … /32для v4, для v6 — до/128; по умолчанию — длина последнего дочернего префикса, иначе/30(v4) //64(v6) в пределах допустимого), строка «Будет создан: 172.20.0.4/30» (обновляется поchangeсписка черезGET …/subnets/next; при отсутствии места — предупреждение и заблокированная кнопка), «Описание», «Статус», «Примечание», флажок «Пул». ИспользуетopenDialog,formBody,fSelect,fInput,dlgFoot,S.dialog. - После успеха: toast «Создан префикс 172.20.0.4/30», родитель раскрывается (
S.collapsed.delete(parent.id)),draw().
Артефакты и тесты
docs/changes/010-next-free-prefix/PLAN.md(копия) иSUMMARY.md;README.md— раздел API/Модель данных: алгоритм и эндпоинты.- Один тест
test_allocate_next_subnetвtests/test_api.py: родитель/24+ ручной/30в начале →/30даёт.4/30;/29даёт.8/29(выравнивание);parent_id= родитель; предпросмотр совпадает с созданием;length≤ длины родителя → 422; после заполнения всех блоков/25→ 409. Плюс мини-проверка чистой функции на IPv6 (в том же тесте).
Проверка
node --check web/app.js;docker compose -p ipam_control_006 up -d --build app;venv/bin/python -m pytest -q.- Браузером (Playwright headless, как в 009): создать
172.20.0.0/24→ вручную172.20.0.0/30→ на родителе «⋯» → «Добавить вложенный (авто)» → в окне «Будет создан: 172.20.0.4/30» → создать 3 раза подряд (.4,.8,.12) → строки в дереве под родителем, родитель раскрыт; размер/29→ следующий выровненный блок; в «Журнале» записиprefix.created. Тестовые данные удалить.