mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-11 21:10:29 +01:00
Implement webhook events system #54
Closed
opened 2025-12-29 15:30:43 +01:00 by adam
·
23 comments
No Branch/Tag Specified
main
update-changelog-comments-docs
feature-removal-issue-type
20911-dropdown
20239-plugin-menu-classes-mutable-state
21097-graphql-id-lookups
feature
fix_module_substitution
20923-dcim-templates
20044-elevation-stuck-lightmode
feature-ip-prefix-link
v4.5-beta1-release
20068-import-moduletype-attrs
20766-fix-german-translation-code-literals
20378-del-script
7604-filter-modifiers-v3
circuit-swap
12318-case-insensitive-uniqueness
20637-improve-device-q-filter
20660-script-load
19724-graphql
20614-update-ruff
14884-script
02496-max-page
19720-macaddress-interface-generic-relation
19408-circuit-terminations-export-templates
20203-openapi-check
fix-19669-api-image-download
7604-filter-modifiers
19275-fixes-interface-bulk-edit
fix-17794-get_field_value_return_list
11507-show-aggregate-and-rir-on-api
9583-add_column_specific_search_field_to_tables
v4.5.0
v4.4.10
v4.4.9
v4.5.0-beta1
v4.4.8
v4.4.7
v4.4.6
v4.4.5
v4.4.4
v4.4.3
v4.4.2
v4.4.1
v4.4.0
v4.3.7
v4.4.0-beta1
v4.3.6
v4.3.5
v4.3.4
v4.3.3
v4.3.2
v4.3.1
v4.3.0
v4.2.9
v4.3.0-beta2
v4.2.8
v4.3.0-beta1
v4.2.7
v4.2.6
v4.2.5
v4.2.4
v4.2.3
v4.2.2
v4.2.1
v4.2.0
v4.1.11
v4.1.10
v4.1.9
v4.1.8
v4.2-beta1
v4.1.7
v4.1.6
v4.1.5
v4.1.4
v4.1.3
v4.1.2
v4.1.1
v4.1.0
v4.0.11
v4.0.10
v4.0.9
v4.1-beta1
v4.0.8
v4.0.7
v4.0.6
v4.0.5
v4.0.3
v4.0.2
v4.0.1
v4.0.0
v3.7.8
v3.7.7
v4.0-beta2
v3.7.6
v3.7.5
v4.0-beta1
v3.7.4
v3.7.3
v3.7.2
v3.7.1
v3.7.0
v3.6.9
v3.6.8
v3.6.7
v3.7-beta1
v3.6.6
v3.6.5
v3.6.4
v3.6.3
v3.6.2
v3.6.1
v3.6.0
v3.5.9
v3.6-beta2
v3.5.8
v3.6-beta1
v3.5.7
v3.5.6
v3.5.5
v3.5.4
v3.5.3
v3.5.2
v3.5.1
v3.5.0
v3.4.10
v3.4.9
v3.5-beta2
v3.4.8
v3.5-beta1
v3.4.7
v3.4.6
v3.4.5
v3.4.4
v3.4.3
v3.4.2
v3.4.1
v3.4.0
v3.3.10
v3.3.9
v3.4-beta1
v3.3.8
v3.3.7
v3.3.6
v3.3.5
v3.3.4
v3.3.3
v3.3.2
v3.3.1
v3.3.0
v3.2.9
v3.2.8
v3.3-beta2
v3.2.7
v3.3-beta1
v3.2.6
v3.2.5
v3.2.4
v3.2.3
v3.2.2
v3.2.1
v3.2.0
v3.1.11
v3.1.10
v3.2-beta2
v3.1.9
v3.2-beta1
v3.1.8
v3.1.7
v3.1.6
v3.1.5
v3.1.4
v3.1.3
v3.1.2
v3.1.1
v3.1.0
v3.0.12
v3.0.11
v3.0.10
v3.1-beta1
v3.0.9
v3.0.8
v3.0.7
v3.0.6
v3.0.5
v3.0.4
v3.0.3
v3.0.2
v3.0.1
v3.0.0
v2.11.12
v3.0-beta2
v2.11.11
v2.11.10
v3.0-beta1
v2.11.9
v2.11.8
v2.11.7
v2.11.6
v2.11.5
v2.11.4
v2.11.3
v2.11.2
v2.11.1
v2.11.0
v2.10.10
v2.10.9
v2.11-beta1
v2.10.8
v2.10.7
v2.10.6
v2.10.5
v2.10.4
v2.10.3
v2.10.2
v2.10.1
v2.10.0
v2.9.11
v2.10-beta2
v2.9.10
v2.10-beta1
v2.9.9
v2.9.8
v2.9.7
v2.9.6
v2.9.5
v2.9.4
v2.9.3
v2.9.2
v2.9.1
v2.9.0
v2.9-beta2
v2.8.9
v2.9-beta1
v2.8.8
v2.8.7
v2.8.6
v2.8.5
v2.8.4
v2.8.3
v2.8.2
v2.8.1
v2.8.0
v2.7.12
v2.7.11
v2.7.10
v2.7.9
v2.7.8
v2.7.7
v2.7.6
v2.7.5
v2.7.4
v2.7.3
v2.7.2
v2.7.1
v2.7.0
v2.6.12
v2.6.11
v2.6.10
v2.6.9
v2.7-beta1
Solcon-2020-01-06
v2.6.8
v2.6.7
v2.6.6
v2.6.5
v2.6.4
v2.6.3
v2.6.2
v2.6.1
v2.6.0
v2.5.13
v2.5.12
v2.6-beta1
v2.5.11
v2.5.10
v2.5.9
v2.5.8
v2.5.7
v2.5.6
v2.5.5
v2.5.4
v2.5.3
v2.5.2
v2.5.1
v2.5.0
v2.4.9
v2.5-beta2
v2.4.8
v2.5-beta1
v2.4.7
v2.4.6
v2.4.5
v2.4.4
v2.4.3
v2.4.2
v2.4.1
v2.4.0
v2.3.7
v2.4-beta1
v2.3.6
v2.3.5
v2.3.4
v2.3.3
v2.3.2
v2.3.1
v2.3.0
v2.2.10
v2.3-beta2
v2.2.9
v2.3-beta1
v2.2.8
v2.2.7
v2.2.6
v2.2.5
v2.2.4
v2.2.3
v2.2.2
v2.2.1
v2.2.0
v2.1.6
v2.2-beta2
v2.1.5
v2.2-beta1
v2.1.4
v2.1.3
v2.1.2
v2.1.1
v2.1.0
v2.0.10
v2.1-beta1
v2.0.9
v2.0.8
v2.0.7
v2.0.6
v2.0.5
v2.0.4
v2.0.3
v2.0.2
v2.0.1
v2.0.0
v2.0-beta3
v1.9.6
v1.9.5
v2.0-beta2
v1.9.4-r1
v1.9.3
v2.0-beta1
v1.9.2
v1.9.1
v1.9.0-r1
v1.8.4
v1.8.3
v1.8.2
v1.8.1
v1.8.0
v1.7.3
v1.7.2-r1
v1.7.1
v1.7.0
v1.6.3
v1.6.2-r1
v1.6.1-r1
1.6.1
v1.6.0
v1.5.2
v1.5.1
v1.5.0
v1.4.2
v1.4.1
v1.4.0
v1.3.2
v1.3.1
v1.3.0
v1.2.2
v1.2.1
v1.2.0
v1.1.0
v1.0.7-r1
v1.0.7
v1.0.6
v1.0.5
v1.0.4
v1.0.3-r1
v1.0.3
1.0.0
Labels
Clear labels
beta
breaking change
complexity: high
complexity: low
complexity: medium
needs milestone
netbox
pending closure
plugin candidate
pull-request
severity: high
severity: low
severity: medium
status: accepted
status: backlog
status: blocked
status: duplicate
status: needs owner
status: needs triage
status: revisions needed
status: under review
topic: GraphQL
topic: Internationalization
topic: OpenAPI
topic: UI/UX
topic: cabling
topic: event rules
topic: htmx navigation
topic: industrialization
topic: migrations
topic: plugins
topic: scripts
topic: templating
topic: testing
type: bug
type: deprecation
type: documentation
type: feature
type: housekeeping
type: translation
Mirrored from GitHub Pull Request
No Label
status: accepted
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: starred/netbox#54
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @mdlayher on GitHub (Jun 28, 2016).
To allow NetBox to become more extensible while still keeping its code clean and focused, a webhooks system could be implemented, enabling hooks to fire when certain events take place.
This might be a ways out, but it's worth thinking about at least. Proposed as an idea to solve #63 and #77 in a more generic way.
@bellwood commented on GitHub (Jun 28, 2016):
WHMCS has a pretty nice hook system that one could look to...
Basically every system action would have a pre and post action hook...
Hooks are defined for these actions and a numerical id assigned on each so they can be processed in a specific order
Reference: http://docs.whmcs.com/Hooks
@x-zeroflux-x commented on GitHub (Jun 29, 2016):
Perhaps using the webhooks or an API to allow connection to PowerDNS to handle PTR records for ip allocations.
@rdujardin commented on GitHub (Aug 1, 2016):
A simple solution may be to use the django signals : https://docs.djangoproject.com/en/1.9/ref/signals/
We can make a system which listens to these signals on every model, and also to the signal emitted when a HTTP request happens (to detect every access to netbox). This system could then call some user-specified scripts, in a specific folder for instance.
What about it ?
@rdujardin commented on GitHub (Aug 4, 2016):
Hi,
I have made a hook system for Netbox, you can currently see it on my fork, in the branch "hooks".
It's very simple : there is a folder "userscripts", every script in it is automatically loaded and can connect receivers to django signals. No action is needed to load the scripts. No action will be needed to add support for future models.
Django doesn't emit any signal on a bulk edit, so I created a signal for it, which can be listened the same way.
Below is the doc I have written for this system and an example of a listening script (that I included in the commit), please tell me what you think about it.
@rdujardin commented on GitHub (Aug 19, 2016):
I added to userscripts a logging utility and the ability to be called through a HTTP request. It allows for instance to call a script daily from a cron instead of each time an event occurs.
New doc :
New example script :
https://github.com/rdujardin/netbox/blob/hooks/netbox/userscripts/example.py
@Armadill0 commented on GitHub (Nov 8, 2016):
This would be such a great feature to connect many external systems. An implementation would be awesome! 😃
@xenuser commented on GitHub (Nov 8, 2016):
I think what @rdujardin proposed is exactly what most organizations are looking for. It is also exactly what I need. I like the idea to use Django signals and hopefully this approach would be something what jeremystretch could easily adopt.
This feature request is very important since it allows the integration of third-party tools without having to write a specific "module" for each third-party solution/vendor.
In my organization, we'd use such a feature to automatically handover data from NetBox to our own APIs which take care of LDAP, DNS, third-party support tools etc.
In my eyes, this GitHub issue actually covers something I'd consider as a "basic feature" for a decent DCIM solution. And who knows - maybe some people didn't switch to NetBox yet because they are waiting for exactly this feature.
@WilliamMarti commented on GitHub (Nov 14, 2016):
Another +1 for this feature. In my current custom IPAM solution, we have code that automatically talks to the Infoblox API to add DNS entries if desired.
The ability to extend the Netbox functionality to other systems would be very valuable.
@a1466d44-d3dc-4c0b-90c7-315b088731d7 commented on GitHub (Feb 13, 2017):
+1 from me, as I'd like to let netbox talk to Microsoft DNS and DHCP Server vis custom scripts/plugins/API calls/ect.
@lampwins commented on GitHub (Mar 2, 2017):
Also throwing in support for this. One very big use cases I have for this ties in with #150 (vlan port mapping). When a vlan is changed on a port, a webhook is fired off to an automation platform to enact that change.
@jsenecal commented on GitHub (Mar 3, 2017):
This could be achieved through celery tasks but that would also increase
installation complexity...
On Thu, Mar 2, 2017, 15:32 John Anderson, notifications@github.com wrote:
@bmsmithvb commented on GitHub (Mar 8, 2017):
I love seeing features like this become implemented into such powerful software.
Is there any chance that anyone has an example of what an integration with WHMCS or PHPIPAM would look like? Not a coder myself...
@jeremystretch commented on GitHub (Mar 23, 2017):
I'd like to implement this in a manner similar to how GitHub does it: Select the models and events you're interested, and tell NetBox what URL to hit when something happens. NetBox would then POST a JSON representation of that event to the specified URL.
As @rdujardin suggested, we can leverage Django signals to accomplish this. However, I'd like to store webhooks in the database rather than as user-created scripts. We should still be able to support customization by templatizing the request body. I'm curious what people would expect the body of the POST request to look like.
@mdlayher commented on GitHub (Mar 23, 2017):
I would probably expect something like the contents of an object that was just added, updated, or deleted, as well as some metadata that talks about the type of event, the time it occurred, the user who performed the action, etc.
JSON POST body all the way.
@lampwins commented on GitHub (Oct 18, 2017):
I am going to start a WIP for this. Here is my initial though process.
To make this worthwhile it is going to require a persistent back end worker, so these actions happen in the background and we can handle things like retries in a proper manor. My thought is to build this functionality out but not enable it by default as this is really a power user feature anyway.
I want to use python-rq for its relative simplicity. This means, to enable the feature, the user will have to install redis and somehow start the background worker process (most likely just as an option when starting the server).
I do like @jeremystretch's idea to model it after github's implementation. Ideally the event triggers will be fired by Django signals and the background worker will figure out what to do based on the webhook(s) the user defines.
@lampwins commented on GitHub (Oct 23, 2017):
As a follow up to where I am at with this. I have a fully functional implementation and am working out some of the details now. In an effort to adhere to the 80/20 rule here is how I have chosen to implement it:
These models may have zero or more webhooks registered them (similar to how custom fields work):
Each of these models may have webhooks registered to one or more of these signals:
post_savesignal withcreated=True)post_savesignal withcreated=False)post_deletesignal)The Webhook model resides in extras and is accessed through the admin site. It looks like this:
namepayload_url- URL to make POST request totype_create- Webhook is registered to create signalstype_update- Webhook is registered to update signalstype_delete- Webhook is registered to delete signalsenabledinsecure_ssl- When true, the POST request will not verify ssl (use with obvious caution)secret- When provided, the POST request will include aX-Hook-Signatureheader which is a HMAC (sha512) hex digest of the request body using the secret as the key.obj_types- ManyToMany relation on the models this webhook is registered to. Again this is the same way custom fields work.content_type- ContentType for the POST request. Eitherapplication/jsonorapplication/x-www-form-urlencoded.The actual POST request is formatted like this using the model's api serializer (using application/json content type):
I consider this an "advanced feature" and like the napalm integration, takes a little bit of extra effort from the user to enable it. That is to say, the internal functionality is all there but ships disabled.
To enable it, the user will have to preform these steps:
pip install django-rq. This also installs python-rq and all of its dependancies like python redis.I am currently coming up with an elegant way to start the python-rq background worker process from within the same supervisor unit that netbox uses in the install docs. In my current implementation the user would have to start this separately.
If the feature is not enabled in the configuration, netbox will never try to import
django-rqor any of its dependancies nor try to connect to redis so if a user does not wish to use this feature, nothing will have changed and the user will not have to take any action. It will also never register any signals so there is zero performance hit when the feature is disabled.When enabled there are some safeguards to ensure everything is ready. Namely when netbox is starting up it will ensure
django-rqis installed and a redis connection can be made.On startup, each app registers applicable models to the two generic signal receiver functions in
extras.webhooksinside of the app'sready()method. When the extras app is ready, it pulls all webhooks out of the database and stores them in a cache. This is the native django local memory cashe, so there is no added installation/upgrade complexity here. This is important because we don't want to hit the database for webhook matching criteria each time a model signal is fired. The extras app also registers a special signal on the webhook model so that any update will refresh the webhook cache.When a model signal is fired, it is received by the appropriate receiver function (post_save vs. post_delete). If the webhook feature is enabled, we retrieve the webhook cache and look for any and all webhooks which meet the criteria for this signal. For each matching webhook found, enqueue a job into django-rq (the 'default' job queue is the only one implemented). The background worker will then process anything in the queue. The POST request is built and then made to the payload url. If a good status code (from the python requests definition) is returned, the job is successful, otherwise it has failed and django-rq puts the job in the "failed" queue.
Django-rq also implements an admin view which allows the user to view the status of the job queue(s). There the result of jobs can be seen and failed jobs can be manually retried.
This is my first iteration but it seems to be working quite well :)
@madkiss commented on GitHub (Nov 13, 2017):
Hi and thank you for your wonderful work! Am I right to assume that this code is supposed to allow the calling of external utilities (e.g. certain binaries on the Netbox host) in case somebody performs a certain change using the API or the web interface? Based on the code, I am not sure how I would create the "external command" redirection ...
@lampwins commented on GitHub (Nov 14, 2017):
@madkiss not exactly. You are correct in that it is used to interact with external systems. However it is mean to interface with HTTP systems. Specifically the way #1640 is implemented, when registered models are created/updated/deleted, a HTTP POST request is made to one or more user configured URLs. The payload of the request includes the model data and the event type.
@lampwins commented on GitHub (Jan 30, 2018):
So my first iteration over this (https://github.com/digitalocean/netbox/pull/1640) was very inlightning. Several things came up that need to be addressed in a further implementation attempt.
Basically, I used a property on each Model to link it to its respective API serializer and then dynamically imported those when needed. The django rest framework requires the request be passed in when constructing a serializer with a model instance (for
HyperLinkedentities). This is an issue because the built in django signals forpost_saveandpost_deletedo not include the request object. I worked around this by constructing an empty request object and passing it into the serializer. The result is that theHyperLinkedentity urls were relative, i.e. did not contain the hostname.The main issue resides in the bulk update views. These views use the queryset
updatemethod to perform bulk updates. This generates a single SQL query and thus bypasses any django signal that would otherwise be dispatched for each model instance.Another "annoying" implementation detail was the way in which I registered the models to the signal receivers. Ultimately this can be refactored but I am not sure of the related performance hit.
django-rq worked out nicely and provided just the base level of functionality needed to implement the job queue without adding the tremendous complexity and overhead that celery imposes.
In all, the implementation in the PR was "ok" for a very generic approach and in fact I am using it in another project in which the above considerations are not of the same concern to me at this time.
I think for this to truly succeed, we should actually take a step back and create our own signals. Otherwise we would need a hack to use the build in
post_savesignal in a bulk update operation. I feel we should actually model the types of events a network operator would actually care about. For instance, I might not care that an individual interface has been updated, but instead I want to receive a notification that the interface configuration for a device has changed. So when you do a bulk update on a set of interfaces, you do not get events for each interface, but instead one event for the device.@jeremystretch I see you are considering this feature request. Do you have any thoughts? I would be more than happy to dig into this more with you.
@lampwins commented on GitHub (Feb 2, 2018):
After some thought, I came back to this and refactored my first iteration. I think I solved the bulk operation problem and have reopened #1640. If anyone has the time, please try that branch out and let know what you think.
@jeremystretch commented on GitHub (May 30, 2018):
Merged @lampwins' PR #1641 into
develop-2.4, so this can be officially closed out! 🎉Please open new issues for any feature requests or bugs related to webhooks from this point forward.
@mdlayher commented on GitHub (May 30, 2018):
Yes! Thanks so much @lampwins for doing this! Can't wait to give this a shot in our production environment.
@ghost commented on GitHub (Sep 21, 2018):
@WilliamMarti we are also using infoblox in our organization, we are planning to adopting with netbox so Could you elaborate bit more about your setup & how compatible/easy to ingrate infoblox - netbox, it will really great to hear 👍
Thanks.