NetBox Event Rules: webhook только при смене статуса
АйТи Фреш
Linux, Docker и DevOps

Как заставить Event Rule NetBox срабатывать только при смене статуса, а не при каждом сохранении

Автор: , директор ООО «АйТи-Фреш» · · ~13 мин чтения
Webhook в NetBox срабатывает только при реальной смене статуса устройства, а не при каждом сохранении карточки
Условие «статус active» и условие «статус только что изменился» — не одно и то же.

Если webhook в NetBox срабатывает на каждое сохранение устройства, хотя условие проверяет только статус — вы столкнулись не с багом, а с тем, как вообще устроены условия Event Rule: они проверяют состояние объекта на момент события, а не факт изменения конкретного поля. В NetBox 4.7 для этого добавили честные операторы changed и unchanged — разбираю, как их использовать правильно.

Почему условие status=active стреляет при любом сохранении

Классическая ошибка при настройке Event Rule в NetBox — завести правило с условием вида {"attr": "status.value", "value": "active"} и ожидать, что webhook сработает именно в момент, когда устройство переходит в статус active. На деле условие проверяет текущее состояние объекта в момент события update, а не то, изменилось ли конкретное поле. Если устройство уже находится в active и администратор просто поправил, скажем, серийный номер или описание, событие update всё равно произойдёт, условие status.value == active всё равно останется истинным — и webhook снова уйдёт, хотя по смыслу задачи ничего важного не изменилось.

Для инфраструктуры, где автоматизация на базе NetBox через webhook запускает реальные действия — регистрацию в системе мониторинга Zabbix, создание задачи в service desk, синхронизацию с внешней CMDB — это не косметическая проблема. Повторные срабатывания создают шум в интеграции и в худшем случае многократно запускают операции над рабочей сетью, которые должны были выполниться один раз, в момент реального перехода состояния.

Мейнтейнеры NetBox в подобных обсуждениях (например, в GitHub Discussion #21492, февраль 2026) прямо формулируют это ограничение простыми словами: условие оценивается относительно состояния объекта в момент события update, оно не означает «сработать только при изменении конкретного поля». Это не баг и не недоработка — так изначально была спроектирована система условий, рассчитанная на проверку состояния, а не на отслеживание дельты. Понимание этой границы и привело к тому, что в NetBox отдельно появились snapshot-операторы, о которых я расскажу дальше — именно они закрывают разрыв между «текущее состояние» и «то, что реально изменилось». Такое же разграничение «текущее состояние» и «событие изменения» я держу в голове, когда настраиваю триггеры в Zabbix 7.0 LTS: там точно так же легко перепутать условие на пороговое значение с условием на переход через это значение и получить лавину одинаковых уведомлений вместо одного полезного.

Сравнение двух условий Event Rule в NetBox: обычная проверка статуса и проверка с оператором changed
Оператор changed превращает «сейчас active» в «только что стал active».

Как устроены условия Event Rule и откуда взялась путаница

Условие в NetBox — это JSON-объект с ключами attr (какой атрибут проверяем), value (эталонное значение для сравнения), необязательным op (оператор сравнения, по умолчанию eq) и необязательным negate (инверсия результата). Стандартные операторы — eq, gt, gte, lt, lte, in, contains и появившийся тоже в 4.7 regex — сравнивают текущие данные объекта с заданным value. Ни один из них по своей природе не умеет отвечать на вопрос «а изменилось ли это значение только что» — они смотрят только на текущее состояние.

До NetBox 4.7 условия Event Rule вообще не видели предыдущего состояния объекта — они оценивались только по текущим данным. В том самом обсуждении #21492 на GitHub пользователь пытался собрать условие {"and": [{"attr": "status.value", "value": "active"}, {"attr": "snapshots.prechange.status.value", "value": "inventory"}]} — «текущий статус active, а был inventory» — и выяснил, что в условиях снапшоты не поддерживаются. Мейнтейнер предложил единственный рабочий на тот момент путь: принимать все события update, а сравнение делать на стороне приёмника. В теле webhook NetBox и раньше передавал блок snapshots с ключами prechange и postchange, так что скрипт-получатель мог сам сравнить статус «до» и «после» и молча выбросить лишние события.

Недостатки такого обходного пути очевидны: логику фильтрации приходится писать и поддерживать в каждом получателе отдельно, NetBox всё равно ставит в очередь и отправляет каждый лишний webhook, а если получатель — готовый сервис без возможности вставить свой код (service desk, мессенджер, система учёта), фильтровать просто негде. Плюс к этому у устройства в DCIM NetBox семь статусов (offline, active, planned, staged, failed, inventory, decommissioning), и объект может попасть в active практически из любого из них — любая самописная фильтрация на приёмнике быстро обрастает частными случаями. Именно эту дыру закрыли в 4.7: сравнение снапшотов переехало прямо в условия правила.

Операторы changed и unchanged в NetBox 4.7

NetBox 4.7.0 (release notes датированы 2 сентября 2026 года) добавил в условия Event Rule два новых snapshot-оператора — changed и unchanged, а заодно синтаксис snapshots.prechange.<attr> и snapshots.postchange.<attr>, которым любой стандартный оператор может прочитать значение «до» или «после». Операторы changed/unchanged сравнивают значение атрибута между двумя снапшотами напрямую, одним условием. Ключевая особенность, которую легко упустить: операторы changed и unchanged **не принимают** ключ value — они не сравнивают атрибут с конкретным эталоном, они сравнивают его сам с собой в двух состояниях.

Условие для «webhook срабатывает только когда статус реально поменялся именно на active» теперь собирается так:

{
  "and": [
    {"attr": "status.value", "value": "active"},
    {"attr": "status", "op": "changed"}
  ]
}

Первое условие проверяет текущее состояние (status.value == active), второе — оператором changed — требует, чтобы поле status действительно отличалось между до и после. Вместе они означают ровно то, что нужно: сработать только в момент перехода в active, а не при любом сохранении устройства, которое уже находится в этом статусе. Одна оговорка из документации: для события создания объекта снапшота prechange нет, и changed считается истинным для любого атрибута. Если правило подписано ещё и на создание объектов, то устройство, заведённое сразу в статусе active, тоже вызовет webhook — чаще всего это как раз то, что нужно, но об этом стоит помнить.

Обратите внимание на разницу путей: в первом условии — status.value (choice-поле в формате REST API, объект с value/label), во втором — просто status (в контексте снапшота choice-поле представлено как обычная строка, не объект). Перепутать путь — самая частая причина, почему правило с changed не срабатывает вовсе.
Схема разницы пути к полю статуса в обычном условии REST API и в условии снапшота Event Rule NetBox
В снапшоте статус — просто строка, а не объект с value и label.

Почему в snapshot нельзя использовать .value так же, как в обычных условиях

Снапшоты в NetBox хранятся в формате сериализатора модели, а не в формате REST API — это важное и не всегда очевидное отличие. В обычном условии, которое сравнивается с текущим объектом через REST-представление, поле выбора (choice field) вроде статуса выглядит как вложенный объект {"value": "active", "label": "Active"}, поэтому путь к нему пишут как status.value. А в снапшотах то же самое поле представлено просто строкой "active" без вложенности — значит, путь для операторов снапшота нужно писать как status, без .value на конце.

Если по ошибке написать {"attr": "status.value", "op": "changed"} вместо {"attr": "status", "op": "changed"}, правило не упадёт при сохранении — JSON валиден, форма его примет. Но суффикс .value в снапшоте не разрешается, условие по документации «fails closed»: правило не срабатывает, а в Python-логгер netbox.event_rules пишется ошибка. Никакого отдельного журнала в веб-интерфейсе для этого нет — ошибку видно только в логах сервера NetBox (там, куда у вас настроен LOGGING), поэтому, если webhook после правки условия замолчал, первым делом смотрю именно туда. negate такую ошибку не «переворачивает» в совпадение — неразрешимый путь остаётся несрабатыванием.

Это же различие путей касается и других choice-полей объекта, не только статуса — роли устройства, платформы или, скажем, статуса IP-адреса. Правило простое и одинаковое для всех: если условие относится к текущему состоянию объекта — используйте формат REST API с .value; если условие использует changed/unchanged или прямо обращается к snapshots.prechange/snapshots.postchange — путь к choice-полю пишется без .value, потому что снапшот сериализует поле как обычную строку.

Как выбрать между changed, unchanged и старым способом через snapshots.prechange

Оператор changed подходит, когда важен сам факт перехода атрибута в нужное состояние, а не то, из какого именно состояния он пришёл — например, «уведомить, когда устройство стало active», независимо от того, было оно до этого planned, offline или staged. Это самый частый и самый правильный случай использования.

Прямое обращение к снапшоту через snapshots.prechange.status (именно без .value) нужно, если важен конкретный переход именно между двумя названными статусами — скажем, отличать «offline → active» (реальный ввод в эксплуатацию) от «staged → active» (плановое обновление уже работающего оборудования), и реагировать на них по-разному. В таком случае changed слишком грубый — он не различает, откуда именно пришло значение, поэтому условие собирается так: {"and": [{"attr": "status.value", "value": "active"}, {"attr": "snapshots.prechange.status", "value": "offline"}]}.

Оператор unchanged, обратный changed, полезен в противоположном сценарии: например, правило должно сработать при сохранении объекта, только если статус НЕ поменялся, а изменилось что-то другое — это нужно реже, но встречается в сценариях аудита конфигурации, когда важно отследить правки атрибутов без смены жизненного цикла объекта.

На практике я советую клиентам не пытаться с первого раза угадать идеальное условие, а проверять правило на тестовом объекте сразу после настройки: меняю статус в интерфейсе NetBox туда и обратно, правлю посторонние поля и смотрю на тестовом приёмнике webhook, какие запросы пришли, а в логе netbox.event_rules — нет ли ошибок разбора условия. Пять минут ручной проверки на тестовом устройстве экономят часы разбора задваивающихся задач в внешней системе спустя неделю — примерно так же, как я проверяю каждую новую интеграцию Zammad с Active Directory и почтой на тестовом тикете перед тем, как включать её на боевой очереди обращений.

Кейс: охотничий магазин «ОхотГрад», webhook, который дублировал заявки в 1С

В охотничьем магазине «ОхотГрад» (14 рабочих мест) NetBox использовался для учёта кассового и сетевого оборудования точек продаж. Event Rule на изменение устройства с условием status.value == active был настроен на отправку webhook во внутренний сервис, который создавал задачу в 1С на выезд инженера для финальной настройки нового оборудования на точке. Схема выглядела логично на бумаге: устройство завели в NetBox со статусом planned, инженер привёз и подключил кассу, перевёл статус в active — и по этому событию должна была прилетать одна-единственная задача на первичную настройку.

Проблема вскрылась не сразу: администратор магазина периодически правил в NetBox серийные номера и инвентарные метки уже работающих касс — банальная актуализация данных, статус при этом не менялся, оставался active. Но каждое такое сохранение всё равно проходило по условию status.value == active и заново отправляло webhook, а сервис на другом конце снова создавал задачу «настроить оборудование на точке», хотя оборудование давно настроено и работает. За месяц таким образом накопилось больше десятка дублирующих задач, которые инженеры закрывали вручную как ошибочные.

Решение заняло один вечер: я обновил условие правила до {"and": [{"attr": "status.value", "value": "active"}, {"attr": "status", "op": "changed"}]}, используя оператор changed, доступный после обновления магазина до NetBox 4.7. После этого webhook стал приходить ровно один раз — в момент, когда устройство реально переходит в active, а не при каждой правке карточки уже работающей кассы. За две недели после изменения ни одной дублирующей задачи в 1С не появилось, притом что администратор продолжил так же часто редактировать инвентарные данные оборудования.

Отдельно порекомендовал администратору магазина завести такое же условие с changed для остальных правил, которые он настраивал по образцу первого — до этого случая в NetBox было ещё два похожих Event Rule на других объектах (сетевое оборудование точек и POS-терминалы), скопированных с тем же условием без changed. Я проверил оба и в одном из них нашёл ту же проблему на ранней стадии — webhook туда шёл в сервис уведомлений в Telegram, просто дублирующиеся уведомления никто не воспринимал как баг, списывая на «шумный NetBox». Заодно сверил учёт самого сетевого оборудования магазина в NetBox с реальным парком — это отдельная задача, но она у меня обычно идёт в комплекте с документированием инфраструктуры на NetBox для небольшой компании, если клиент уже завёл систему, но не довёл структуру объектов до конца.

Цифры кейса охотничьего магазина: устранение дублирующих задач в 1С после добавления оператора changed в Event Rule NetBox
Один добавленный оператор в условии убрал больше десятка ложных задач в месяц.

Частые вопросы

Почему webhook в NetBox срабатывает на каждое сохранение, а не только на смену статуса?

Потому что обычное условие проверяет текущее состояние объекта на момент события, а не то, изменилось ли конкретное поле. Если объект уже в нужном статусе, любое его сохранение — даже правка другого поля — снова удовлетворяет условию.

Какой оператор нужен, чтобы условие сработало только на реальную смену поля?

changed — он сравнивает значение атрибута в снапшотах до и после изменения. В паре с обычным условием на нужное значение он даёт срабатывание именно на переход в конкретный статус.

Можно ли передать value вместе с оператором changed?

Нет, операторы changed и unchanged не принимают ключ value — они не сравнивают атрибут с эталоном, а проверяют сам факт изменения (или неизменности) между pre-change и post-change снапшотами.

Почему в условии для changed путь status, а не status.value?

Снапшоты хранятся в формате сериализатора модели, а не REST API: choice-поля вроде статуса представлены там простой строкой, без вложенного объекта value/label. Путь .value относится только к обычным условиям над текущим объектом.

С какой версии NetBox доступны операторы changed и unchanged?

С версии 4.7.0 (release notes от 2 сентября 2026 года). До неё условия Event Rule не видели предыдущего состояния объекта: сравнивать snapshots.prechange и postchange из тела webhook приходилось на стороне приёмника.

Когда лучше использовать snapshots.prechange вместо changed?

Когда важен конкретный переход между двумя названными статусами, а не любое изменение — например, отличить offline → active от staged → active. Путь пишется без .value: snapshots.prechange.status. changed не различает, из какого состояния пришло значение.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи