Почему NetBox неправильно показывает заполненность подсети: когда нужны Container, is_pool и mark_utilized
Если родительская подсеть в NetBox показывает 0 % заполненности, хотя дочерние подсети выделены и частично заняты, — дело не в баге, а в статусе префикса: обычный (active) префикс считает заведённые в NetBox IP-адреса внутри своего диапазона и не видит дочерние префиксы как таковые, а долю пространства, нарезанного на дочерние подсети, показывает только статус container. Разбираю логику расчёта, зачем нужны is_pool и mark_utilized, и как я развожу эти три механизма в IPAM у клиентов.
Симптом: родительская подсеть висит на 0 %, хотя всё пространство расписано
Классическая ситуация после того, как в компании внедрили NetBox для учёта IT-инфраструктуры: в IPAM заводят крупную подсеть, например 10.20.0.0/24, режут её на несколько /26 или /27 под разные VLAN и сегменты, а сама родительская /24 в списке префиксов продолжает показывать 0 % utilization. Администратор проверяет — дочерние подсети на месте, часть адресов в них занята, а сводная цифра по «большой» подсети как будто не замечает вложенной структуры вовсе.
Первая реакция — счесть это багом отображения и полезть чинить кэш или пересчитывать что-нибудь вручную. На деле проблема почти всегда не в NetBox, а в том, что у родительской подсети не выставлен статус container: у обычного статуса (active, reserved, deprecated и других) есть чёткое правило подсчёта, которое просто не смотрит на дочерние префиксы, а считает только объекты IP-адресов в своём диапазоне. И если адреса в NetBox ещё не заводились — а на старте внедрения так почти всегда, сначала переносят префиксы, — то с точки зрения этого правила /24 действительно занята на 0 %, сколько бы подсетей в ней ни было нарезано.
Как на самом деле считается utilization: два разных правила для двух ролей префикса
В модели Prefix у NetBox статус container означает, что префикс существует именно как контейнер для организации дочерних подсетей, а не как сегмент с реальными адресами. И расчёт заполненности для него принципиально другой: container-префикс учитывает объём адресного пространства, «нарезанного» в дочерние префиксы, — независимо от того, насколько заняты сами эти дочерние подсети. Если внутри container-префикса 10.20.0.0/24 выделен дочерний 10.20.0.0/26, container покажет 25 % — ровно долю пространства, а не долю занятых IP-адресов в этой четверти, даже если в /26 занят один-единственный адрес или не занято ни одного.
Все остальные статусы префикса — active, reserved, deprecated — считают заполненность иначе и полностью игнорируют дочерние префиксы как объекты: в расчёт идут реально созданные объекты IP-адресов в диапазоне префикса (той же VRF) плюс диапазоны IPRange с флагом mark_utilized, которые засчитываются целиком, — относительно числа адресов в подсети. Важная тонкость: адрес, заведённый внутри дочерней /26, одновременно лежит и в диапазоне родительской /24, поэтому родитель в статусе active его тоже посчитает. Отсюда два разных сценария. Если адреса в дочерних подсетях заводятся как объекты, родитель в active покажет долю занятых адресов — например, 12 %, хотя всё пространство уже расписано по сегментам. Если адреса не заводятся вовсе (живут только в DHCP), родитель законно покажет 0 %. Ни в одном из случаев active-процент не отвечает на вопрос «сколько места осталось под новые подсети» — на него отвечает только container.
Разработчики NetBox явно подтверждали эту логику в обсуждении на GitHub: пользователь спрашивал, почему родительский префикс не отражает уровень заполненности вложенных подсетей, и получил ответ, что «активные» префиксы считают IP-адреса и не учитывают дочерние префиксы, а «контейнерные» — ровно наоборот, считают долю пространства, отданного в дочерние префиксы, вне зависимости от их собственной заполненности.
Ловушка: container и active с одинаковым диапазоном одновременно
Есть ещё один частый источник «нулевых» процентов — когда в базе одновременно существуют container-префикс и active-префикс с абсолютно тем же диапазоном адресов (например, оба на 10.20.0.0/24, потому что кто-то завёл рабочую подсеть поверх контейнера, не заметив дубликата). В таком случае расчёт для container-префикса по умолчанию исключает из подсчёта дочерние префиксы, полностью совпадающие с ним по границам, — и итоговая цифра тоже может уйти в 0 %, хотя визуально кажется, что всё выделено.
В обсуждении на GitHub с похожим кейсом (когда container и active-префикс совпадали по диапазону) разработчики подтвердили именно эту механику исключения точных дублей из подсчёта и предложили обходной путь через кастомный фильтр запроса (сравнение net_contained_or_equal вместо net_contained), — но это уже вмешательство в код фильтрации, а не штатная настройка через интерфейс. В актуальном коде это видно напрямую: для container выбираются дочерние префиксы строго внутри его границ (net_contained), а равный по размеру в эту выборку не попадает. Проще и правильнее для админа — не заводить дубликат: если подсеть нужна одновременно и как административный контейнер, и как рабочий сегмент с адресами, для рабочего слоя создаётся именно дочерний префикс меньшего размера, а не второй префикс с идентичными границами.
Вторая похожая ловушка — VRF. Container считает только дочерние префиксы из той же VRF, что и он сам. Если родительская /16 заведена в глобальной таблице, а дочерние подсети филиалов разнесены по VRF (или наоборот), container покажет 0 % или заметно меньше реального, хотя дочерние префиксы видны в дереве по адресам. Я при разборе таких жалоб первым делом сравниваю столбец VRF у родителя и у детей: либо переношу родителя в ту же VRF, либо держу отдельный container на каждую VRF — второй вариант честнее, если адресные пространства VRF действительно пересекаются.
Что делают is_pool и IPRange.mark_utilized
Флаг is_pool у префикса решает другую задачу — не про иерархию, а про то, какие адреса внутри одной подсети вообще считаются занимаемыми. По умолчанию первый адрес подсети (сетевой) и последний (широковещательный) не участвуют в расчёте доступных адресов, как и положено по классике IP-адресации. Если включить is_pool, оба этих адреса становятся доступными для использования — NetBox сам явно указывает, что это годится для документирования пулов NAT, где крайние адреса диапазона реально назначаются устройствам, а не служат служебными границами подсети. На процент это тоже влияет: в актуальных версиях для IPv4-префикса без is_pool знаменатель — размер подсети минус два адреса (для /24 это 254), а с is_pool — полный размер (256). Для /31 и /32 все адреса считаются используемыми и без флага, для IPv6 знаменатель — полный размер префикса. Разница в пару процентов на маленьких подсетях как раз и объясняет, почему «одинаково заполненные» /28 с флагом и без показывают разные цифры.
IPRange — отдельная сущность для документирования произвольного диапазона адресов (например, части подсети, выделенной под DHCP-пул или NAT), и у него собственный флаг mark_utilized: если включить, диапазон при подсчёте заполненности родительского префикса будет считаться занятым на 100 % независимо от того, сколько конкретных IP-адресов реально создано внутри него как объекты. Без этого флага заполненность диапазона считается по фактически заведённым в нём адресам — и если вы просто зарезервировали диапазон под DHCP, но не завели в NetBox ни одного адреса из него как объект, IPRange честно покажет 0 %, даже если в реальности DHCP-сервер раздал из него половину адресов.
Таблица: что учитывается в расчёте заполненности для каждой конфигурации
Собрал итоговую логику в одну таблицу — именно её я показываю клиентам, когда они впервые сталкиваются с «неправильными» процентами в IPAM:
| Конфигурация | Что учитывается в utilization | Типичный результат |
|---|---|---|
| Prefix, статус active/reserved, без IPRange | Объекты IPAddress в диапазоне (включая лежащие в дочерних подсетях), дочерние префиксы — нет | 0 %, если адреса не заведены, даже при полностью нарезанном пространстве |
| Prefix, статус container | Объём пространства, отданный под дочерние префиксы той же VRF | Доля от размера родителя, не зависит от занятости детей |
| Prefix container + active с тем же диапазоном | Точный дубль по границам исключается из подсчёта | Может показать 0 % при визуально «полном» пространстве |
| Prefix с is_pool | Первый и последний адрес подсети становятся доступными | Расчёт доступных адресов включает сетевой и broadcast |
| IPRange без mark_utilized | Для родителя диапазон не засчитывается, считаются только созданные IPAddress | 0 %, если адреса не заведены как объекты, даже если выданы по DHCP |
| IPRange с mark_utilized | Игнорирует фактические адреса, диапазон = 100 % | Заполненность родителя учитывает весь диапазон целиком |
| Prefix.mark_utilized | Игнорирует и адреса, и дочерние префиксы | Всегда 100 %, ручной override администратора |
Кейс «КнигоДом»: перенумерация склада из-за неверно прочитанного процента
У клиента — сеть книжных магазинов «КнигоДом», 29 рабочих мест в центральном офисе и на складе. При переносе учёта сети в NetBox завели крупную подсеть склада 10.30.4.0/23 в статусе active (по умолчанию при импорте из старой Excel-таблицы), а внутри неё уже руками нарезали пять рабочих /27 под кассовые терминалы, Wi-Fi точки, принт-серверы, видеонаблюдение и резерв. Сетевой инженер, планируя подключение нового участка сортировки, открыл список префиксов, увидел у родительской /23 честные 0 % и решил, что всё адресное пространство свободно. Отдельные IP-адреса в NetBox на тот момент ещё не заводили — из Excel импортировали только префиксы, адреса планировали перенести «следующим этапом».
Он начал выделять из той же /23 ещё один /26 под новый участок — и через несколько дней обнаружил пересечение с уже работающим диапазоном видеонаблюдения: два устройства получили одинаковые IP по DHCP-резервации, и часть камер на складе отвалилась в течение рабочего дня. Причина была ровно в статусе: родительская /23 в active не показывает занятость пространства дочерними /27, сколько бы их ни завели, а адресов-объектов в ней не было ни одного — 0 % в её карточке не значил «пусто», а значил «здесь не заведено ни одного IP и этот префикс не смотрит на детей».
Исправили за один вечер: поменяли статус родительской /23 на container, для диапазона DHCP камер завели отдельный IPRange с включённым mark_utilized (адреса раздаются по DHCP-резервации, а не заводятся в NetBox по одному), а для пула управляющих IP на коммутаторах, где реально используются крайние адреса подсети, включили is_pool. После этого родительский префикс стал честно показывать долю выделенного пространства, а не пустой 0 %, и повторной коллизии за два месяца эксплуатации не было.
Что я проверяю в IPAM, прежде чем доверять проценту заполненности
Правило, которое я завёл после нескольких похожих случаев у разных клиентов: прежде чем читать процент заполненности как факт, смотрю статус префикса. Если это иерархический уровень («сеть филиала», «весь адресный план склада») — статус только container, и точка; если это рабочий сегмент с реальными адресами устройств — active или другой рабочий статус, но тогда я слежу, чтобы адреса реально заводились в NetBox как объекты IPAddress, а не жили только в DHCP-логах, иначе процент всегда будет врать в меньшую сторону.
Для диапазонов DHCP и NAT-пулов сразу завожу IPRange с mark_utilized, а не жду, пока кто-то попробует выделить из «свободного» на вид пространства новый сегмент. И отдельно проверяю на дубликаты границ между container и active-префиксами — это самый неочевидный источник ложных нулей, который не лечится настройками интерфейса, только аккуратной структурой самого адресного плана. Если у вас в IPAM много legacy-подсетей без единой схемы статусов, начать стоит именно с ревизии — это тот же принцип, с которого я начинаю любую документацию IT-инфраструктуры в NetBox: какие префиксы должны быть иерархией, а какие — рабочими сегментами, и уже под это раскладывать is_pool и mark_utilized, а не наоборот.
Частые вопросы
Почему родительская подсеть в NetBox показывает 0 %, хотя все дочерние подсети выделены?
Потому что расчёт заполненности зависит от статуса префикса. Обычные статусы (active, reserved и другие) считают только объекты IPAddress в своём диапазоне и игнорируют дочерние префиксы; если адреса ещё не заведены, выйдет 0 %. Чтобы родитель показывал долю выделенного пространства между детьми, ему нужен статус container — и дети в той же VRF.
Чем отличается заполненность container-префикса от обычного?
Container-префикс считает объём адресного пространства, отданного под дочерние префиксы той же VRF, независимо от заполненности самих детей: дочерний /26 внутри container /24 даёт 25 %. Обычный статус считает заведённые IP-адреса в своём диапазоне (включая лежащие в дочерних подсетях) и IPRange с mark_utilized, а сами дочерние префиксы не учитывает.
Что делает флаг is_pool у префикса?
Он делает первый (сетевой) и последний (широковещательный) адрес подсети доступными для использования вместо служебного резервирования. Подходит для документирования NAT-пулов, где эти крайние адреса реально назначаются устройствам.
Зачем нужен mark_utilized у IPRange, если можно просто завести все адреса вручную?
Mark_utilized заставляет NetBox считать диапазон занятым на 100 % без создания отдельного объекта IPAddress на каждый адрес — удобно для DHCP-пулов и NAT, где адреса выдаются динамически и заводить их по одному нецелесообразно. Без этого флага заполненность диапазона считается только по фактически созданным объектам адресов.
Может ли утилизация показать 0 %, даже если статус выставлен верно?
Да, если внутри базы одновременно существуют container-префикс и обычный префикс с абсолютно тем же диапазоном адресов — такой точный дубль по умолчанию исключается из подсчёта заполненности контейнера. Решение — не заводить второй префикс с идентичными границами, а использовать для рабочего слоя дочерний префикс меньшего размера.
Есть ли способ вручную зафиксировать 100 % заполненности префикса, не считая реальные адреса?
Да, поле Prefix.mark_utilized: если включить, префикс всегда будет показывать 100 % утилизации независимо от числа дочерних объектов или адресов внутри него — это ручной override для случаев, когда подсеть выведена из оборота, но формально ещё занята.
Источники
- NetBox Labs Docs — Prefixes — Официальное описание модели Prefix: значение статуса container («exists merely as a container for organizing child prefixes»), поведение is_pool (первый и последний адрес подсети становятся доступными, сценарий NAT pools) и mark_utilized (форсирует 100 % независимо от дочерних объектов). https://netboxlabs.com/docs/netbox/models/ipam/prefix/
- NetBox Labs Docs — IP Ranges — Официальное описание модели IPRange: назначение диапазона, поле Mark Utilized (диапазон считается занятым на 100 % независимо от числа реально созданных IP-адресов внутри него) и Mark Populated (диапазон считается полностью населённым при расчёте доступного пространства). https://netboxlabs.com/docs/netbox/models/ipam/iprange/
- GitHub netbox-community/netbox — Discussion #8985 «Prefix Utilization» — Проверено разъяснение разработчика: active-префиксы считают только собственные IP-адреса и не видят дочерние префиксы; container-префиксы считают долю пространства, отданного в дочерние префиксы, независимо от их собственной заполненности; пример /26 внутри container /24 = 25 %. https://github.com/netbox-community/netbox/discussions/8985
- GitHub netbox-community/netbox — Discussion #5834 «Prefix utilization is 0 % when the container prefix and active prefix are same» — Проверен кейс совпадения диапазонов container и active-префикса: точный дубль по границам исключается из подсчёта заполненности контейнера, обходной путь через изменение фильтра запроса (net_contained_or_equal). https://github.com/netbox-community/netbox/discussions/5834
- NetBox — исходный код ipam/models/ip.py и release notes — Проверено по исходнику Prefix.get_utilization(): mark_utilized → 100 %; container — дочерние префиксы net_contained той же VRF; прочие статусы — IPAddress в диапазоне той же VRF + IPRange с mark_utilized; знаменатель IPv4 без is_pool = size−2. Актуальная версия — v4.7.1 от 15.09.2026. https://github.com/netbox-community/netbox/blob/main/netbox/ipam/models/ip.py



