Add more Event Rule Conditions #10548

Open
opened 2025-12-29 21:33:00 +01:00 by adam · 7 comments
Owner

Originally created by @Hardi100 on GitHub (Dec 4, 2024).

NetBox version

v4.1.7

Feature type

Change to existing functionality

Triage priority

I volunteer to perform this work (if approved)

Proposed functionality

The Event Rule condition only checks the objects data, not the value it had before. https://github.com/netbox-community/netbox/blob/develop/netbox/extras/events.py#L90. This proposal could save a lot of unnecessary Webhooks or Scripts, if the data, that the condition checks doesn't change.

Use case

I want to create a device 1x in an external application, if the device has status active.

  1. I create a device with status Planned.
  2. I update the device and set the status to active.
  3. I update the device and give it a description.
  4. I Update the device and set the status to Offline.

I create an Event Rule, on update send a Webhook if the status of the device is active.
My Event Rule condition states:

{
    "attr": "status.value",
    "value": "active"
}

The event hook is sent on step 2 & 3. But on step 3 the webhook is executed unnecessary because the Device was created in step 2 in the external system.

Possible new condition syntax:

{
  "and": [
    {
      "attr": "status.value",
      "value": "active"
    },
    {
      "attr": "snapshots.prechange.status.value",
      "value": "snapshots.postchange.status.value",
      "op": "eq",
      "negate": true
    }
  ]
}

or

{
    "attr": "status.value",
    "value": "active",
    "changed": true
}

This rule should check if the status is active, and check if the value of the status has changed. Only if the status has been changed and set to active, the webhook is executed.

Database changes

None

External dependencies

None

Originally created by @Hardi100 on GitHub (Dec 4, 2024). ### NetBox version v4.1.7 ### Feature type Change to existing functionality ### Triage priority I volunteer to perform this work (if approved) ### Proposed functionality The Event Rule condition only checks the objects data, not the value it had before. https://github.com/netbox-community/netbox/blob/develop/netbox/extras/events.py#L90. This proposal could save a lot of unnecessary Webhooks or Scripts, if the data, that the condition checks doesn't change. ### Use case I want to create a device 1x in an external application, if the device has status active. 1. I create a device with status Planned. 2. I update the device and set the status to active. 3. I update the device and give it a description. 4. I Update the device and set the status to Offline. I create an Event Rule, `on update` send a Webhook if the status of the device is active. My Event Rule condition states: ```json { "attr": "status.value", "value": "active" } ``` The event hook is sent on step 2 & 3. But on step 3 the webhook is executed unnecessary because the Device was created in step 2 in the external system. Possible new condition syntax: ```json { "and": [ { "attr": "status.value", "value": "active" }, { "attr": "snapshots.prechange.status.value", "value": "snapshots.postchange.status.value", "op": "eq", "negate": true } ] } ``` or ```json { "attr": "status.value", "value": "active", "changed": true } ``` This rule should check if the status is active, and check if the value of the status has changed. Only if the status has been changed and set to active, the webhook is executed. ### Database changes None ### External dependencies None
Author
Owner

@ravenrs commented on GitHub (Jul 4, 2025):

@jeremystretch @rboucher-me Hi, could you please assign it to me, I want to use this functionallity to, I don't think that we need to add this complex logic like

"attr": "snapshots.prechange.status.value",
"value": "snapshots.postchange.status.value",

because now we haven't function to get value by path in "value" field, so If I add functionality to add snapshots to example above can be solved with AND/OR logic.

@ravenrs commented on GitHub (Jul 4, 2025): @jeremystretch @rboucher-me Hi, could you please assign it to me, I want to use this functionallity to, I don't think that we need to add this complex logic like > "attr": "snapshots.prechange.status.value", > "value": "snapshots.postchange.status.value", because now we haven't function to get value by path in "value" field, so If I add functionality to add snapshots to example above can be solved with AND/OR logic.
Author
Owner

@ravenrs commented on GitHub (Jul 7, 2025):

@jeremystretch here is my local test for created commits.
Here is site:

 {
     "id": 1,
     "url": "http://127.0.0.1:8000/api/dcim/sites/1/",
     "display_url": "http://127.0.0.1:8000/dcim/sites/1/",
     "display": "test_site",
     "name": "test_site",
     "slug": "test_site",
     "status": {
         "value": "planned",
         "label": "Planned"
     },
     "region": null,
     "group": null,
     "tenant": null,
     "facility": "",
     "time_zone": null,
     "description": "",
     "physical_address": "",
     "shipping_address": "",
     "latitude": null,
     "longitude": null,
     "comments": "test1",
     "asns": [],
     "tags": [],
     "custom_fields": {},
     "created": "2025-06-02T13:59:27.877175Z",
     "last_updated": "2025-07-07T10:58:15.923192Z",
     "circuit_count": 0,
     "device_count": 1,
     "prefix_count": 0,
     "rack_count": 1,
     "virtualmachine_count": 0,
     "vlan_count": 0
 }

Here is events:

 {
     "count": 2,
     "next": null,
     "previous": null,
     "results": [
         {
             "id": 1,
             "url": "http://127.0.0.1:8000/api/extras/event-rules/1/",
             "display_url": "http://127.0.0.1:8000/extras/event-rules/1/",
             "display": "event_with_condition",
             "object_types": [
                 "dcim.site"
             ],
             "name": "event_with_condition",
             "enabled": true,
             "event_types": [
                 "object_created",
                 "object_updated"
             ],
             "conditions": {
                 "and": [
                     {
                         "attr": "snapshots.prechange.status",
                         "value": "planned"
                     },
                     {
                         "attr": "snapshots.postchange.status",
                         "value": "active"
                     }
                 ]
             },
             "action_type": {
                 "value": "webhook",
                 "label": "Webhook"
             },
             "action_object_type": "extras.webhook",
             "action_object_id": 1,
             "action_object": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/extras/webhooks/1/",
                 "display": "test_wh",
                 "name": "test_wh",
                 "description": ""
             },
             "description": "",
             "custom_fields": {},
             "tags": [],
             "created": "2025-06-02T14:13:43.197651Z",
             "last_updated": "2025-07-07T10:55:59.444469Z"
         },
         {
             "id": 2,
             "url": "http://127.0.0.1:8000/api/extras/event-rules/2/",
             "display_url": "http://127.0.0.1:8000/extras/event-rules/2/",
             "display": "event_without_condition",
             "object_types": [
                 "dcim.site"
             ],
             "name": "event_without_condition",
             "enabled": true,
             "event_types": [
                 "object_created",
                 "object_updated"
             ],
             "conditions": null,
             "action_type": {
                 "value": "webhook",
                 "label": "Webhook"
             },
             "action_object_type": "extras.webhook",
             "action_object_id": 1,
             "action_object": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/extras/webhooks/1/",
                 "display": "test_wh",
                 "name": "test_wh",
                 "description": ""
             },
             "description": "",
             "custom_fields": {},
             "tags": [],
             "created": "2025-07-07T10:53:42.004179Z",
             "last_updated": "2025-07-07T10:55:18.429526Z"
         }
     ]
 }


WH:


 {
     "id": 1,
     "url": "http://127.0.0.1:8000/api/extras/webhooks/1/",
     "display_url": "http://127.0.0.1:8000/extras/webhooks/1/",
     "display": "test_wh",
     "name": "test_wh",
     "description": "",
     "payload_url": "http://localhost:9000/",
     "http_method": "POST",
     "http_content_type": "application/json",
     "additional_headers": "",
     "body_template": "{\r\n  \"prechange_status\": \"{{ snapshots.prechange.status }}\",\r\n  \"postchange_status\": \"{{ snapshots.postchange.status }}\"\r\n}",
     "secret": "",
     "ssl_verification": true,
     "ca_file_path": null,
     "custom_fields": {},
     "tags": [],
     "created": "2025-06-02T14:07:20.321354Z",
     "last_updated": "2025-07-07T11:38:09.149694Z"
 }

Run webhook-receiver and rqworker:
RQ

./manage.py rqworker default high low
11:39:06 Worker rq:worker:7f3ee649493d4b70a45102fba7350dc7 started with PID 31919, version 2.4.0
11:39:06 Subscribing to channel rq:pubsub:7f3ee649493d4b70a45102fba7350dc7
11:39:06 *** Listening on default, high, low...
11:39:07 Cleaning registries for queue: default
11:39:07 Cleaning registries for queue: high
11:39:08 Cleaning registries for queue: low

WHR:

./manage.py webhook_receiver
Listening on port http://localhost:9000. Stop with CONTROL-C.

Update site's comment, we expect only one webhook from event_without_condition:
Change log:

            "id": 36,
             "url": "http://127.0.0.1:8000/api/core/object-changes/36/",
             "display_url": "http://127.0.0.1:8000/core/changelog/36/",
             "display": "DCIM | site test_site updated by password",
             "time": "2025-07-07T11:40:31.032848Z",
             "user": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/users/users/1/",
                 "display": "password",
                 "username": "password"
             },
             "user_name": "password",
             "request_id": "e0df2a59-58bc-41ac-ba15-eedce2ec0330",
             "action": {
                 "value": "update",
                 "label": "Updated"
             },
             "changed_object_type": "dcim.site",
             "changed_object_id": 1,
             "changed_object": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/dcim/sites/1/",
                 "display": "test_site",
                 "name": "test_site",
                 "slug": "test_site",
                 "description": ""
             },
             "prechange_data": {
                 "asns": [],
                 "name": "test_site",
                 "slug": "test_site",
                 "tags": [],
                 "group": null,
                 "region": null,
                 "status": "planned",
                 "tenant": null,
                 "comments": "test1",
                 "facility": "",
                 "latitude": null,
                 "longitude": null,
                 "time_zone": null,
                 "description": "",
                 "custom_fields": {},
                 "physical_address": "",
                 "shipping_address": ""
             },
             "postchange_data": {
                 "asns": [],
                 "name": "test_site",
                 "slug": "test_site",
                 "tags": [],
                 "group": null,
                 "region": null,
                 "status": "planned",
                 "tenant": null,
                 "comments": "test condition update comment only",
                 "facility": "",
                 "latitude": null,
                 "longitude": null,
                 "time_zone": null,
                 "description": "",
                 "custom_fields": {},
                 "physical_address": "",
                 "shipping_address": ""
             }
         },

RQ:

11:40:32 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('e0df2a59-58bc-41ac-ba15-eedce2ec0330'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:40:32.102118+00:00', username='password') (bb317911-3be1-49c4-9b44-556e52f37f51)
11:40:34 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('e0df2a59-58bc-41ac-ba15-eedce2ec0330'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:40:32.102118+00:00', username='password') job in 0:00:00.593711s on worker 7f3ee649493d4b70a45102fba7350dc7
11:40:34 default: Job OK (bb317911-3be1-49c4-9b44-556e52f37f51)
11:40:34 Result is kept for 500 seconds

WHR:

 [1] Mon, 07 Jul 2025 11:40:33 GMT 127.0.0.1 "POST / HTTP/1.1" 200 -
 Host: localhost:9000
 Accept-Encoding: identity
 User-Agent: python-urllib3/2.5.0
 Content-Type: application/json
 Content-Length: 69
 
 {
     "prechange_status": "planned",
     "postchange_status": "planned"
 }
 Completed request #1
 ------------

Changing status from planned to active, expect 2 webhooks.
Change log:


         {
             "id": 37,
             "url": "http://127.0.0.1:8000/api/core/object-changes/37/",
             "display_url": "http://127.0.0.1:8000/core/changelog/37/",
             "display": "DCIM | site test_site updated by password",
             "time": "2025-07-07T11:42:32.715888Z",
             "user": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/users/users/1/",
                 "display": "password",
                 "username": "password"
             },
             "user_name": "password",
             "request_id": "8db3edbb-c668-431a-8292-ac5a05a09e31",
             "action": {
                 "value": "update",
                 "label": "Updated"
             },
             "changed_object_type": "dcim.site",
             "changed_object_id": 1,
             "changed_object": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/dcim/sites/1/",
                 "display": "test_site",
                 "name": "test_site",
                 "slug": "test_site",
                 "description": ""
             },
             "prechange_data": {
                 "asns": [],
                 "name": "test_site",
                 "slug": "test_site",
                 "tags": [],
                 "group": null,
                 "region": null,
                 "status": "planned",
                 "tenant": null,
                 "comments": "test condition update comment only",
                 "facility": "",
                 "latitude": null,
                 "longitude": null,
                 "time_zone": null,
                 "description": "",
                 "custom_fields": {},
                 "physical_address": "",
                 "shipping_address": ""
             },
             "postchange_data": {
                 "asns": [],
                 "name": "test_site",
                 "slug": "test_site",
                 "tags": [],
                 "group": null,
                 "region": null,
                 "status": "active",
                 "tenant": null,
                 "comments": "test condition update comment only",
                 "facility": "",
                 "latitude": null,
                 "longitude": null,
                 "time_zone": null,
                 "description": "",
                 "custom_fields": {},
                 "physical_address": "",
                 "shipping_address": ""
             }
         },

WHR:


 [2] Mon, 07 Jul 2025 11:42:35 GMT 127.0.0.1 "POST / HTTP/1.1" 200 -
 Host: localhost:9000
 Accept-Encoding: identity
 User-Agent: python-urllib3/2.5.0
 Content-Type: application/json
 Content-Length: 68
 
 {
     "prechange_status": "planned",
     "postchange_status": "active"
 }
 Completed request #2
 ------------
 [3] Mon, 07 Jul 2025 11:42:37 GMT 127.0.0.1 "POST / HTTP/1.1" 200 -
 Host: localhost:9000
 Accept-Encoding: identity
 User-Agent: python-urllib3/2.5.0
 Content-Type: application/json
 Content-Length: 68
 
 {
     "prechange_status": "planned",
     "postchange_status": "active"
 }
 Completed request #3
 ------------

RQ

 11:42:34 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_with_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:33.777408+00:00', username='password') (f27dd0da-ab16-4993-a69c-a5773cd744ab)
 11:42:35 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_with_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:33.777408+00:00', username='password') job in 0:00:00.612253s on worker 7f3ee649493d4b70a45102fba7350dc7
 11:42:35 default: Job OK (f27dd0da-ab16-4993-a69c-a5773cd744ab)
 11:42:35 Result is kept for 500 seconds
 11:42:36 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:34.267364+00:00', username='password') (a98f9e8a-365d-4959-9d1d-0a26ee536443)
 11:42:37 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:34.267364+00:00', username='password') job in 0:00:01.021593s on worker 7f3ee649493d4b70a45102fba7350dc7
 11:42:37 default: Job OK (a98f9e8a-365d-4959-9d1d-0a26ee536443)
 11:42:37 Result is kept for 500 seconds

Change status from active to planned. expect only one wh:
Change log:


         {
             "id": 38,
             "url": "http://127.0.0.1:8000/api/core/object-changes/38/",
             "display_url": "http://127.0.0.1:8000/core/changelog/38/",
             "display": "DCIM | site test_site updated by password",
             "time": "2025-07-07T11:45:44.031827Z",
             "user": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/users/users/1/",
                 "display": "password",
                 "username": "password"
             },
             "user_name": "password",
             "request_id": "58a67337-6b97-41c0-a978-20c1d45c8ba9",
             "action": {
                 "value": "update",
                 "label": "Updated"
             },
             "changed_object_type": "dcim.site",
             "changed_object_id": 1,
             "changed_object": {
                 "id": 1,
                 "url": "http://127.0.0.1:8000/api/dcim/sites/1/",
                 "display": "test_site",
                 "name": "test_site",
                 "slug": "test_site",
                 "description": ""
             },
             "prechange_data": {
                 "asns": [],
                 "name": "test_site",
                 "slug": "test_site",
                 "tags": [],
                 "group": null,
                 "region": null,
                 "status": "active",
                 "tenant": null,
                 "comments": "test condition update comment only",
                 "facility": "",
                 "latitude": null,
                 "longitude": null,
                 "time_zone": null,
                 "description": "",
                 "custom_fields": {},
                 "physical_address": "",
                 "shipping_address": ""
             },
             "postchange_data": {
                 "asns": [],
                 "name": "test_site",
                 "slug": "test_site",
                 "tags": [],
                 "group": null,
                 "region": null,
                 "status": "planned",
                 "tenant": null,
                 "comments": "test condition update comment only",
                 "facility": "",
                 "latitude": null,
                 "longitude": null,
                 "time_zone": null,
                 "description": "",
                 "custom_fields": {},
                 "physical_address": "",
                 "shipping_address": ""
             }
         },

WHR:

 [4] Mon, 07 Jul 2025 11:45:46 GMT 127.0.0.1 "POST / HTTP/1.1" 200 -
 Host: localhost:9000
 Accept-Encoding: identity
 User-Agent: python-urllib3/2.5.0
 Content-Type: application/json
 Content-Length: 68
 
 {
     "prechange_status": "active",
     "postchange_status": "planned"
 }
 Completed request #4
 ------------

RQ:

11:42:37 Result is kept for 500 seconds
11:45:45 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('58a67337-6b97-41c0-a978-20c1d45c8ba9'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:45:45.063734+00:00', username='password') (26150f8a-71cb-4ecd-b47e-b6c7b5d15eb9)
11:45:47 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('58a67337-6b97-41c0-a978-20c1d45c8ba9'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:45:45.063734+00:00', username='password') job in 0:00:01.042723s on worker 7f3ee649493d4b70a45102fba7350dc7
11:45:47 default: Job OK (26150f8a-71cb-4ecd-b47e-b6c7b5d15eb9)
11:45:47 Result is kept for 500 seconds

I can't create pull request without assignation.

@ravenrs commented on GitHub (Jul 7, 2025): @jeremystretch here is my local test for created commits. **Here is site:** ``` { "id": 1, "url": "http://127.0.0.1:8000/api/dcim/sites/1/", "display_url": "http://127.0.0.1:8000/dcim/sites/1/", "display": "test_site", "name": "test_site", "slug": "test_site", "status": { "value": "planned", "label": "Planned" }, "region": null, "group": null, "tenant": null, "facility": "", "time_zone": null, "description": "", "physical_address": "", "shipping_address": "", "latitude": null, "longitude": null, "comments": "test1", "asns": [], "tags": [], "custom_fields": {}, "created": "2025-06-02T13:59:27.877175Z", "last_updated": "2025-07-07T10:58:15.923192Z", "circuit_count": 0, "device_count": 1, "prefix_count": 0, "rack_count": 1, "virtualmachine_count": 0, "vlan_count": 0 } ``` **Here is events:** ``` { "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "url": "http://127.0.0.1:8000/api/extras/event-rules/1/", "display_url": "http://127.0.0.1:8000/extras/event-rules/1/", "display": "event_with_condition", "object_types": [ "dcim.site" ], "name": "event_with_condition", "enabled": true, "event_types": [ "object_created", "object_updated" ], "conditions": { "and": [ { "attr": "snapshots.prechange.status", "value": "planned" }, { "attr": "snapshots.postchange.status", "value": "active" } ] }, "action_type": { "value": "webhook", "label": "Webhook" }, "action_object_type": "extras.webhook", "action_object_id": 1, "action_object": { "id": 1, "url": "http://127.0.0.1:8000/api/extras/webhooks/1/", "display": "test_wh", "name": "test_wh", "description": "" }, "description": "", "custom_fields": {}, "tags": [], "created": "2025-06-02T14:13:43.197651Z", "last_updated": "2025-07-07T10:55:59.444469Z" }, { "id": 2, "url": "http://127.0.0.1:8000/api/extras/event-rules/2/", "display_url": "http://127.0.0.1:8000/extras/event-rules/2/", "display": "event_without_condition", "object_types": [ "dcim.site" ], "name": "event_without_condition", "enabled": true, "event_types": [ "object_created", "object_updated" ], "conditions": null, "action_type": { "value": "webhook", "label": "Webhook" }, "action_object_type": "extras.webhook", "action_object_id": 1, "action_object": { "id": 1, "url": "http://127.0.0.1:8000/api/extras/webhooks/1/", "display": "test_wh", "name": "test_wh", "description": "" }, "description": "", "custom_fields": {}, "tags": [], "created": "2025-07-07T10:53:42.004179Z", "last_updated": "2025-07-07T10:55:18.429526Z" } ] } ``` **WH:** ``` { "id": 1, "url": "http://127.0.0.1:8000/api/extras/webhooks/1/", "display_url": "http://127.0.0.1:8000/extras/webhooks/1/", "display": "test_wh", "name": "test_wh", "description": "", "payload_url": "http://localhost:9000/", "http_method": "POST", "http_content_type": "application/json", "additional_headers": "", "body_template": "{\r\n \"prechange_status\": \"{{ snapshots.prechange.status }}\",\r\n \"postchange_status\": \"{{ snapshots.postchange.status }}\"\r\n}", "secret": "", "ssl_verification": true, "ca_file_path": null, "custom_fields": {}, "tags": [], "created": "2025-06-02T14:07:20.321354Z", "last_updated": "2025-07-07T11:38:09.149694Z" } ``` **Run webhook-receiver and rqworker: RQ** ``` ./manage.py rqworker default high low 11:39:06 Worker rq:worker:7f3ee649493d4b70a45102fba7350dc7 started with PID 31919, version 2.4.0 11:39:06 Subscribing to channel rq:pubsub:7f3ee649493d4b70a45102fba7350dc7 11:39:06 *** Listening on default, high, low... 11:39:07 Cleaning registries for queue: default 11:39:07 Cleaning registries for queue: high 11:39:08 Cleaning registries for queue: low ``` **WHR:** ./manage.py webhook_receiver Listening on port http://localhost:9000. Stop with CONTROL-C. **Update site's comment, we expect only one webhook from event_without_condition: Change log:** ``` "id": 36, "url": "http://127.0.0.1:8000/api/core/object-changes/36/", "display_url": "http://127.0.0.1:8000/core/changelog/36/", "display": "DCIM | site test_site updated by password", "time": "2025-07-07T11:40:31.032848Z", "user": { "id": 1, "url": "http://127.0.0.1:8000/api/users/users/1/", "display": "password", "username": "password" }, "user_name": "password", "request_id": "e0df2a59-58bc-41ac-ba15-eedce2ec0330", "action": { "value": "update", "label": "Updated" }, "changed_object_type": "dcim.site", "changed_object_id": 1, "changed_object": { "id": 1, "url": "http://127.0.0.1:8000/api/dcim/sites/1/", "display": "test_site", "name": "test_site", "slug": "test_site", "description": "" }, "prechange_data": { "asns": [], "name": "test_site", "slug": "test_site", "tags": [], "group": null, "region": null, "status": "planned", "tenant": null, "comments": "test1", "facility": "", "latitude": null, "longitude": null, "time_zone": null, "description": "", "custom_fields": {}, "physical_address": "", "shipping_address": "" }, "postchange_data": { "asns": [], "name": "test_site", "slug": "test_site", "tags": [], "group": null, "region": null, "status": "planned", "tenant": null, "comments": "test condition update comment only", "facility": "", "latitude": null, "longitude": null, "time_zone": null, "description": "", "custom_fields": {}, "physical_address": "", "shipping_address": "" } }, ``` **RQ:** ``` 11:40:32 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('e0df2a59-58bc-41ac-ba15-eedce2ec0330'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:40:32.102118+00:00', username='password') (bb317911-3be1-49c4-9b44-556e52f37f51) 11:40:34 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('e0df2a59-58bc-41ac-ba15-eedce2ec0330'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:40:32.102118+00:00', username='password') job in 0:00:00.593711s on worker 7f3ee649493d4b70a45102fba7350dc7 11:40:34 default: Job OK (bb317911-3be1-49c4-9b44-556e52f37f51) 11:40:34 Result is kept for 500 seconds ``` **WHR:** ``` [1] Mon, 07 Jul 2025 11:40:33 GMT 127.0.0.1 "POST / HTTP/1.1" 200 - Host: localhost:9000 Accept-Encoding: identity User-Agent: python-urllib3/2.5.0 Content-Type: application/json Content-Length: 69 { "prechange_status": "planned", "postchange_status": "planned" } Completed request #1 ------------ ``` **Changing status from planned to active, expect 2 webhooks. Change log:** ``` { "id": 37, "url": "http://127.0.0.1:8000/api/core/object-changes/37/", "display_url": "http://127.0.0.1:8000/core/changelog/37/", "display": "DCIM | site test_site updated by password", "time": "2025-07-07T11:42:32.715888Z", "user": { "id": 1, "url": "http://127.0.0.1:8000/api/users/users/1/", "display": "password", "username": "password" }, "user_name": "password", "request_id": "8db3edbb-c668-431a-8292-ac5a05a09e31", "action": { "value": "update", "label": "Updated" }, "changed_object_type": "dcim.site", "changed_object_id": 1, "changed_object": { "id": 1, "url": "http://127.0.0.1:8000/api/dcim/sites/1/", "display": "test_site", "name": "test_site", "slug": "test_site", "description": "" }, "prechange_data": { "asns": [], "name": "test_site", "slug": "test_site", "tags": [], "group": null, "region": null, "status": "planned", "tenant": null, "comments": "test condition update comment only", "facility": "", "latitude": null, "longitude": null, "time_zone": null, "description": "", "custom_fields": {}, "physical_address": "", "shipping_address": "" }, "postchange_data": { "asns": [], "name": "test_site", "slug": "test_site", "tags": [], "group": null, "region": null, "status": "active", "tenant": null, "comments": "test condition update comment only", "facility": "", "latitude": null, "longitude": null, "time_zone": null, "description": "", "custom_fields": {}, "physical_address": "", "shipping_address": "" } }, ``` **WHR:** ``` [2] Mon, 07 Jul 2025 11:42:35 GMT 127.0.0.1 "POST / HTTP/1.1" 200 - Host: localhost:9000 Accept-Encoding: identity User-Agent: python-urllib3/2.5.0 Content-Type: application/json Content-Length: 68 { "prechange_status": "planned", "postchange_status": "active" } Completed request #2 ------------ [3] Mon, 07 Jul 2025 11:42:37 GMT 127.0.0.1 "POST / HTTP/1.1" 200 - Host: localhost:9000 Accept-Encoding: identity User-Agent: python-urllib3/2.5.0 Content-Type: application/json Content-Length: 68 { "prechange_status": "planned", "postchange_status": "active" } Completed request #3 ------------ ``` **RQ** ``` 11:42:34 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_with_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:33.777408+00:00', username='password') (f27dd0da-ab16-4993-a69c-a5773cd744ab) 11:42:35 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_with_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:33.777408+00:00', username='password') job in 0:00:00.612253s on worker 7f3ee649493d4b70a45102fba7350dc7 11:42:35 default: Job OK (f27dd0da-ab16-4993-a69c-a5773cd744ab) 11:42:35 Result is kept for 500 seconds 11:42:36 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:34.267364+00:00', username='password') (a98f9e8a-365d-4959-9d1d-0a26ee536443) 11:42:37 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('8db3edbb-c668-431a-8292-ac5a05a09e31'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:42:34.267364+00:00', username='password') job in 0:00:01.021593s on worker 7f3ee649493d4b70a45102fba7350dc7 11:42:37 default: Job OK (a98f9e8a-365d-4959-9d1d-0a26ee536443) 11:42:37 Result is kept for 500 seconds ``` **Change status from active to planned. expect only one wh: Change log:** ``` { "id": 38, "url": "http://127.0.0.1:8000/api/core/object-changes/38/", "display_url": "http://127.0.0.1:8000/core/changelog/38/", "display": "DCIM | site test_site updated by password", "time": "2025-07-07T11:45:44.031827Z", "user": { "id": 1, "url": "http://127.0.0.1:8000/api/users/users/1/", "display": "password", "username": "password" }, "user_name": "password", "request_id": "58a67337-6b97-41c0-a978-20c1d45c8ba9", "action": { "value": "update", "label": "Updated" }, "changed_object_type": "dcim.site", "changed_object_id": 1, "changed_object": { "id": 1, "url": "http://127.0.0.1:8000/api/dcim/sites/1/", "display": "test_site", "name": "test_site", "slug": "test_site", "description": "" }, "prechange_data": { "asns": [], "name": "test_site", "slug": "test_site", "tags": [], "group": null, "region": null, "status": "active", "tenant": null, "comments": "test condition update comment only", "facility": "", "latitude": null, "longitude": null, "time_zone": null, "description": "", "custom_fields": {}, "physical_address": "", "shipping_address": "" }, "postchange_data": { "asns": [], "name": "test_site", "slug": "test_site", "tags": [], "group": null, "region": null, "status": "planned", "tenant": null, "comments": "test condition update comment only", "facility": "", "latitude": null, "longitude": null, "time_zone": null, "description": "", "custom_fields": {}, "physical_address": "", "shipping_address": "" } }, ``` **WHR:** ``` [4] Mon, 07 Jul 2025 11:45:46 GMT 127.0.0.1 "POST / HTTP/1.1" 200 - Host: localhost:9000 Accept-Encoding: identity User-Agent: python-urllib3/2.5.0 Content-Type: application/json Content-Length: 68 { "prechange_status": "active", "postchange_status": "planned" } Completed request #4 ------------ ``` **RQ:** ``` 11:42:37 Result is kept for 500 seconds 11:45:45 default: extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('58a67337-6b97-41c0-a978-20c1d45c8ba9'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:45:45.063734+00:00', username='password') (26150f8a-71cb-4ecd-b47e-b6c7b5d15eb9) 11:45:47 Successfully completed extras.webhooks.send_webhook(data={'id': 1, 'url': '/api/dcim/sites/1/', 'display_url': '/dcim/sites/1/', 'di..., event_rule=<EventRule: event_without_condition>, event_type='object_updated', model_name='site', request_id=UUID('58a67337-6b97-41c0-a978-20c1d45c8ba9'), snapshots={'prechange': {'created': '2025-06-02T13:59:27.877Z', 'description': '', 'c..., timestamp='2025-07-07T11:45:45.063734+00:00', username='password') job in 0:00:01.042723s on worker 7f3ee649493d4b70a45102fba7350dc7 11:45:47 default: Job OK (26150f8a-71cb-4ecd-b47e-b6c7b5d15eb9) 11:45:47 Result is kept for 500 seconds ``` I can't create pull request without assignation.
Author
Owner

@ravenrs commented on GitHub (Sep 13, 2025):

@jnovinger Hi, any updates here?

@ravenrs commented on GitHub (Sep 13, 2025): @jnovinger Hi, any updates here?
Author
Owner

@jnovinger commented on GitHub (Sep 13, 2025):

Hey @ravenrs , apologies for missing you volunteering for this one. It's all yours.

Actually, I may have jumped the gun a bit on assigning this. I just noticed it's in backlog and not marked as needing an owner. I will raise this with the other maintainers on Monday and get back to you.

@jnovinger commented on GitHub (Sep 13, 2025): ~~Hey @ravenrs , apologies for missing you volunteering for this one. It's all yours.~~ Actually, I may have jumped the gun a bit on assigning this. I just noticed it's in backlog and not marked as needing an owner. I will raise this with the other maintainers on Monday and get back to you.
Author
Owner

@jnovinger commented on GitHub (Sep 16, 2025):

@ravenrs , we're going to hold off on assigning this for the moment, as it has the combination of being in the backlog (not yet selected for work), needs a milestone determined, and has been marked as high complexity.

It's clear you've already put much thought in to this, so I will be raising it at our next maintainer meeting to see if we can get some movement. Thanks for your patience.

@jnovinger commented on GitHub (Sep 16, 2025): @ravenrs , we're going to hold off on assigning this for the moment, as it has the combination of being in the backlog (not yet selected for work), needs a milestone determined, and has been marked as high complexity. It's clear you've already put much thought in to this, so I will be raising it at our next maintainer meeting to see if we can get some movement. Thanks for your patience.
Author
Owner

@ravenrs commented on GitHub (Sep 17, 2025):

@jnovinger I got it. Since this is a complex task that requires more effort, can we split it into two tickets?

Add snapshot to the data to enable filtering based on previously saved data (pre-change state). We already take snapshots in the code but don't use them. This would be a simple change and, in my opinion, would cover many use cases (including my needs as a NetBox administrator in my company). It would also address the case mentioned in the description. @Hardi100, please share your thoughts. here is the example.

Implement a more complex solution with an extended serializer to retrieve information from the object and use it in both attr and value.

please let me know Im ready to create new ticket and create pull request.

@ravenrs commented on GitHub (Sep 17, 2025): @jnovinger I got it. Since this is a complex task that requires more effort, can we split it into two tickets? Add snapshot to the data to enable filtering based on previously saved data (pre-change state). We already take snapshots in the code but don't use them. This would be a simple change and, in my opinion, would cover many use cases (including my needs as a NetBox administrator in my company). It would also address the case mentioned in the description. @Hardi100, please share your thoughts. here is the [example](https://github.com/netbox-community/netbox/issues/18159#issuecomment-3044712279). Implement a more complex solution with an extended serializer to retrieve information from the object and use it in both attr and value. please let me know Im ready to create new ticket and create pull request.
Author
Owner

@jnovinger commented on GitHub (Sep 19, 2025):

@ravenrs , for the moment, we've decided to keep this in the backlog. Given the magnitude and nature of this feature request, this is something that a maintainer will tackle whenever it is selected for work. We will be sure to evaluate and consider the clearly significant amount of work you have put in to this already.

Thank you again for volunteering and for the work you've already put in.

@jnovinger commented on GitHub (Sep 19, 2025): @ravenrs , for the moment, we've decided to keep this in the backlog. Given the magnitude and nature of this feature request, this is something that a maintainer will tackle whenever it is selected for work. We will be sure to evaluate and consider the clearly significant amount of work you have put in to this already. Thank you again for volunteering and for the work you've already put in.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#10548