Compare commits

..

No commits in common. "main" and "v4.2.9" have entirely different histories.
main ... v4.2.9

546 changed files with 50242 additions and 65382 deletions

View File

@ -15,7 +15,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.3.4 placeholder: v4.2.9
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -27,7 +27,7 @@ body:
attributes: attributes:
label: NetBox Version label: NetBox Version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.3.4 placeholder: v4.2.9
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -8,7 +8,7 @@
</h3> </h3>
<h3> <h3>
:jigsaw: <a href="#jigsaw-creating-plugins">Create a plugin</a> &middot; :jigsaw: <a href="#jigsaw-creating-plugins">Create a plugin</a> &middot;
:briefcase: <a href="#briefcase-looking-for-a-job">Work with us!</a> &middot; :rescue_worker_helmet: <a href="#rescue_worker_helmet-become-a-maintainer">Become a maintainer</a> &middot;
:heart: <a href="#heart-other-ways-to-contribute">Other ideas</a> :heart: <a href="#heart-other-ways-to-contribute">Other ideas</a>
</h3> </h3>
</div> </div>
@ -109,9 +109,21 @@ Do you have an idea for something you'd like to build in NetBox, but might not b
Check out our [plugin development tutorial](https://github.com/netbox-community/netbox-plugin-tutorial) to get started! Check out our [plugin development tutorial](https://github.com/netbox-community/netbox-plugin-tutorial) to get started!
## :briefcase: Looking for a Job? ## :rescue_worker_helmet: Become a Maintainer
At [NetBox Labs](https://netboxlabs.com/), we're always looking for highly skilled and motivated people to join our team. While NetBox is a core part of our product lineup, we have an ever-expanding suite of solutions serving the network automation space. Check out our [current openings](https://netboxlabs.com/careers/) to see if you might be a fit! We're always looking for motivated individuals to join the maintainers team and help drive NetBox's long-term development. Some of our most sought-after skills include:
* Python development with a strong focus on the [Django](https://www.djangoproject.com/) framework
* Expertise working with PostgreSQL databases
* Javascript & TypeScript proficiency
* A knack for web application design (HTML & CSS)
* Familiarity with git and software development best practices
* Excellent attention to detail
* Working experience in the field of network operations & engineering
We generally ask that maintainers dedicate around four hours of work to the project each week on average, which includes both hands-on development and project management tasks such as issue triage. Maintainers are also encouraged (but not required) to attend our bi-weekly Zoom call to catch up on recent items.
Interested? You can contact our lead maintainer, Jeremy Stretch, at jeremy@netbox.dev or on the [NetDev Community Slack](https://netdev.chat/). We'd love to have you on the team!
## :heart: Other Ways to Contribute ## :heart: Other Ways to Contribute

View File

@ -6,9 +6,9 @@
<a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a> <a href="https://github.com/netbox-community/netbox/graphs/contributors"><img src="https://img.shields.io/github/contributors/netbox-community/netbox?color=blue" alt="Contributors" /></a>
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a> <a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-15-blue" alt="Languages supported" /></a> <a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-15-blue" alt="Languages supported" /></a>
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/actions/workflows/ci.yml/badge.svg" alt="CI status" /></a> <a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=main" alt="CI status" /></a>
<p> <p>
<strong><a href="https://netboxlabs.com/community/">NetBox Community</a></strong> | <strong><a href="https://github.com/netbox-community/netbox/">NetBox Community</a></strong> |
<strong><a href="https://netboxlabs.com/netbox-cloud/">NetBox Cloud</a></strong> | <strong><a href="https://netboxlabs.com/netbox-cloud/">NetBox Cloud</a></strong> |
<strong><a href="https://netboxlabs.com/netbox-enterprise/">NetBox Enterprise</a></strong> <strong><a href="https://netboxlabs.com/netbox-enterprise/">NetBox Enterprise</a></strong>
</p> </p>

View File

@ -14,12 +14,6 @@ Administrators are encouraged to adhere to industry best practices concerning th
* Prohibit access to your database from clients other than the NetBox application * Prohibit access to your database from clients other than the NetBox application
* Keep your deployment updated to the most recent stable release * Keep your deployment updated to the most recent stable release
## Compliance Reporting
Please note that security compliance reports (e.g. SOC 2) are provided by NetBox Labs only to customers using NetBox Cloud or NetBox Enterprise. They are not available to users of self-hosted NetBox Community Edition.
If you would like to consider upgrading to NetBox Cloud or Enterprise, please contact `sales@netboxlabs.com`.
## Reporting a Suspected Vulnerability ## Reporting a Suspected Vulnerability
If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so by emailing `security@netboxlabs.com`. Please ensure that your report meets all the following conditions: If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so by emailing `security@netboxlabs.com`. Please ensure that your report meets all the following conditions:

View File

@ -1,6 +1,6 @@
# The Python web framework on which NetBox is built # The Python web framework on which NetBox is built
# https://docs.djangoproject.com/en/stable/releases/ # https://docs.djangoproject.com/en/stable/releases/
Django==5.2.* Django<5.2
# Django middleware which permits cross-domain API requests # Django middleware which permits cross-domain API requests
# https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst # https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst
@ -14,10 +14,6 @@ django-debug-toolbar
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst # https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
django-filter django-filter
# Django Debug Toolbar extension for GraphiQL
# https://github.com/flavors/django-graphiql-debug-toolbar/blob/main/CHANGES.rst
django-graphiql-debug-toolbar
# HTMX utilities for Django # HTMX utilities for Django
# https://django-htmx.readthedocs.io/en/latest/changelog.html # https://django-htmx.readthedocs.io/en/latest/changelog.html
django-htmx django-htmx
@ -46,10 +42,6 @@ django-rich
# https://github.com/rq/django-rq/blob/master/CHANGELOG.md # https://github.com/rq/django-rq/blob/master/CHANGELOG.md
django-rq django-rq
# Provides a variety of storage backends
# https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst
django-storages
# Abstraction models for rendering and paginating HTML tables # Abstraction models for rendering and paginating HTML tables
# https://github.com/jieter/django-tables2/blob/master/CHANGELOG.md # https://github.com/jieter/django-tables2/blob/master/CHANGELOG.md
django-tables2 django-tables2
@ -86,10 +78,6 @@ gunicorn
# https://jinja.palletsprojects.com/changes/ # https://jinja.palletsprojects.com/changes/
Jinja2 Jinja2
# JSON schema validation
# https://github.com/python-jsonschema/jsonschema/blob/main/CHANGELOG.rst
jsonschema
# Simple markup language for rendering HTML # Simple markup language for rendering HTML
# https://python-markdown.github.io/changelog/ # https://python-markdown.github.io/changelog/
Markdown Markdown
@ -112,7 +100,6 @@ nh3
# Fork of PIL (Python Imaging Library) for image processing # Fork of PIL (Python Imaging Library) for image processing
# https://github.com/python-pillow/Pillow/releases # https://github.com/python-pillow/Pillow/releases
# https://pillow.readthedocs.io/en/stable/releasenotes/
Pillow Pillow
# PostgreSQL database adapter for Python # PostgreSQL database adapter for Python
@ -131,22 +118,21 @@ requests
# https://github.com/rq/rq/blob/master/CHANGES.md # https://github.com/rq/rq/blob/master/CHANGES.md
rq rq
# Django app for social-auth-core
# https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md
social-auth-app-django
# Social authentication framework # Social authentication framework
# https://github.com/python-social-auth/social-core/blob/master/CHANGELOG.md # https://github.com/python-social-auth/social-core/blob/master/CHANGELOG.md
social-auth-core social-auth-core
# Django app for social-auth-core
# https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md
social-auth-app-django
# Strawberry GraphQL # Strawberry GraphQL
# https://github.com/strawberry-graphql/strawberry/blob/main/CHANGELOG.md # https://github.com/strawberry-graphql/strawberry/blob/main/CHANGELOG.md
strawberry-graphql strawberry-graphql
# Strawberry GraphQL Django extension # Strawberry GraphQL Django extension
# https://github.com/strawberry-graphql/strawberry-django/releases # https://github.com/strawberry-graphql/strawberry-django/releases
# See #19771 strawberry-graphql-django
strawberry-graphql-django==0.60.0
# SVG image rendering (used for rack elevations) # SVG image rendering (used for rack elevations)
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst # https://github.com/mozman/svgwrite/blob/master/NEWS.rst

View File

@ -329,7 +329,6 @@
"100base-tx", "100base-tx",
"100base-t1", "100base-t1",
"1000base-t", "1000base-t",
"1000base-sx",
"1000base-lx", "1000base-lx",
"1000base-tx", "1000base-tx",
"2.5gbase-t", "2.5gbase-t",

View File

@ -54,7 +54,7 @@ pg_dump --username netbox --password --host localhost -s netbox > netbox_schema.
By default, NetBox stores uploaded files (such as image attachments) in its media directory. To fully replicate an instance of NetBox, you'll need to copy both the database and the media files. By default, NetBox stores uploaded files (such as image attachments) in its media directory. To fully replicate an instance of NetBox, you'll need to copy both the database and the media files.
!!! note !!! note
These operations are not necessary if your installation is utilizing a [remote storage backend](../configuration/system.md#storages). These operations are not necessary if your installation is utilizing a [remote storage backend](../configuration/system.md#storage_backend).
### Archive the Media Directory ### Archive the Media Directory

View File

@ -4,7 +4,7 @@
Default: `None` Default: `None`
Defines a Sentry data source name (DSN) for automated error reporting. `SENTRY_ENABLED` must be `True` for this parameter to take effect. For example: Defines a Sentry data source name (DSN) for automated error reporting. `SENTRY_ENABLED` must be True for this parameter to take effect. For example:
``` ```
SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0" SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
@ -16,7 +16,7 @@ SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
Default: `False` Default: `False`
Set to `True` to enable automatic error reporting via [Sentry](https://sentry.io/). Set to True to enable automatic error reporting via [Sentry](https://sentry.io/).
!!! note !!! note
The `sentry-sdk` Python package is required to enable Sentry integration. The `sentry-sdk` Python package is required to enable Sentry integration.

View File

@ -6,7 +6,7 @@
Default: `True` Default: `True`
Setting this to `False` will disable the GraphQL API. Setting this to False will disable the GraphQL API.
--- ---

View File

@ -57,7 +57,7 @@ Sets content for the top banner in the user interface.
Default: `True` Default: `True`
Enables anonymous census reporting. To opt out of census reporting, set this to `False`. Enables anonymous census reporting. To opt out of census reporting, set this to False.
This data enables the project maintainers to estimate how many NetBox deployments exist and track the adoption of new versions over time. Census reporting effects a single HTTP request each time a worker starts. The only data reported by this function are the NetBox version, Python version, and a pseudorandom unique identifier. This data enables the project maintainers to estimate how many NetBox deployments exist and track the adoption of new versions over time. Census reporting effects a single HTTP request each time a worker starts. The only data reported by this function are the NetBox version, Python version, and a pseudorandom unique identifier.
@ -102,7 +102,7 @@ The maximum size (in bytes) of an incoming HTTP request (i.e. `GET` or `POST` da
Default: `True` Default: `True`
By default, NetBox will prevent the creation of duplicate prefixes and IP addresses in the global table (that is, those which are not assigned to any VRF). This validation can be disabled by setting `ENFORCE_GLOBAL_UNIQUE` to `False`. By default, NetBox will prevent the creation of duplicate prefixes and IP addresses in the global table (that is, those which are not assigned to any VRF). This validation can be disabled by setting `ENFORCE_GLOBAL_UNIQUE` to False.
--- ---
@ -143,7 +143,7 @@ The number of days to retain job results (scripts and reports). Set this to `0`
Default: `False` Default: `False`
Setting this to `True` will display a "maintenance mode" banner at the top of every page. Additionally, NetBox will no longer update a user's "last active" time upon login. This is to allow new logins when the database is in a read-only state. Recording of login times will resume when maintenance mode is disabled. Setting this to True will display a "maintenance mode" banner at the top of every page. Additionally, NetBox will no longer update a user's "last active" time upon login. This is to allow new logins when the database is in a read-only state. Recording of login times will resume when maintenance mode is disabled.
--- ---
@ -181,7 +181,7 @@ Toggle the availability Prometheus-compatible metrics at `/metrics`. See the [Pr
Default: `False` Default: `False`
When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to `True` to prefer IPv4 instead. When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to prefer IPv4 instead.
--- ---

View File

@ -33,21 +33,3 @@ Note that a plugin must be listed in `PLUGINS` for its configuration to take eff
--- ---
## PLUGINS_CATALOG_CONFIG
Default: `{}` (Empty)
This parameter controls how individual plugins are displayed in the plugins catalog under Admin > System > Plugins. Adding a plugin to the `hidden` list will omit that plugin from the catalog. Adding a plugin to the `static` list will display the plugin, but not link to the plugin details or upgrade instructions.
An example configuration is shown below:
```python
PLUGINS_CATALOG_CONFIG = {
'hidden': [
'plugin1',
],
'static': [
'plugin2',
],
}
```

View File

@ -1,6 +1,6 @@
# Remote Authentication Settings # Remote Authentication Settings
The configuration parameters listed here control remote authentication for NetBox. Note that `REMOTE_AUTH_ENABLED` must be `True` in order for these settings to take effect. The configuration parameters listed here control remote authentication for NetBox. Note that `REMOTE_AUTH_ENABLED` must be true in order for these settings to take effect.
--- ---
@ -8,7 +8,7 @@ The configuration parameters listed here control remote authentication for NetBo
Default: `False` Default: `False`
If `True`, NetBox will automatically create groups specified in the `REMOTE_AUTH_GROUP_HEADER` header if they don't already exist. (Requires `REMOTE_AUTH_ENABLED`.) If true, NetBox will automatically create groups specified in the `REMOTE_AUTH_GROUP_HEADER` header if they don't already exist. (Requires `REMOTE_AUTH_ENABLED`.)
--- ---
@ -16,7 +16,7 @@ If `True`, NetBox will automatically create groups specified in the `REMOTE_AUTH
Default: `False` Default: `False`
If `True`, NetBox will automatically create local accounts for users authenticated via a remote service. (Requires `REMOTE_AUTH_ENABLED`.) If true, NetBox will automatically create local accounts for users authenticated via a remote service. (Requires `REMOTE_AUTH_ENABLED`.)
--- ---
@ -43,7 +43,7 @@ The list of groups to assign a new user account when created using remote authen
Default: `{}` (Empty dictionary) Default: `{}` (Empty dictionary)
A mapping of permissions to assign a new user account when created using remote authentication. Each key in the dictionary should be set to a dictionary of the attributes to be applied to the permission, or `None` to allow all objects. (Requires `REMOTE_AUTH_ENABLED` as `True` and `REMOTE_AUTH_GROUP_SYNC_ENABLED` as `False`.) A mapping of permissions to assign a new user account when created using remote authentication. Each key in the dictionary should be set to a dictionary of the attributes to be applied to the permission, or `None` to allow all objects. (Requires `REMOTE_AUTH_ENABLED` as True and `REMOTE_AUTH_GROUP_SYNC_ENABLED` as False.)
--- ---

View File

@ -2,12 +2,12 @@
## ALLOWED_HOSTS ## ALLOWED_HOSTS
This is a list of valid fully-qualified domain names (FQDNs) and/or IP addresses that can be used to reach the NetBox service. Usually this is the same as the hostname for the NetBox server, but can also be different; for example, when using a reverse proxy serving the NetBox website under a different FQDN than the hostname of the NetBox server. To help guard against [HTTP Host header attacks](https://docs.djangoproject.com/en/stable/topics/security/#host-headers-virtual-hosting), NetBox will not permit access to the server via any other hostnames (or IPs). This is a list of valid fully-qualified domain names (FQDNs) and/or IP addresses that can be used to reach the NetBox service. Usually this is the same as the hostname for the NetBox server, but can also be different; for example, when using a reverse proxy serving the NetBox website under a different FQDN than the hostname of the NetBox server. To help guard against [HTTP Host header attacks](https://docs.djangoproject.com/en/3.0/topics/security/#host-headers-virtual-hosting), NetBox will not permit access to the server via any other hostnames (or IPs).
!!! note !!! note
This parameter must always be defined as a list or tuple, even if only a single value is provided. This parameter must always be defined as a list or tuple, even if only a single value is provided.
The value of this option is also used to set `CSRF_TRUSTED_ORIGINS`, which restricts POST requests to the same set of hosts (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)). Keep in mind that NetBox, by default, sets `USE_X_FORWARDED_HOST` to `True`, which means that if you're using a reverse proxy, it's the FQDN used to reach that reverse proxy which needs to be in this list (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts)). The value of this option is also used to set `CSRF_TRUSTED_ORIGINS`, which restricts POST requests to the same set of hosts (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)). Keep in mind that NetBox, by default, sets `USE_X_FORWARDED_HOST` to true, which means that if you're using a reverse proxy, it's the FQDN used to reach that reverse proxy which needs to be in this list (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts)).
Example: Example:
@ -25,30 +25,7 @@ ALLOWED_HOSTS = ['*']
## DATABASE ## DATABASE
!!! warning "Legacy Configuration Parameter" NetBox requires access to a PostgreSQL 13 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
The `DATABASE` configuration parameter is deprecated and will be removed in a future release. Users are advised to adopt the new `DATABASES` (plural) parameter, which allows for the configuration of multiple databases.
See the [`DATABASES`](#databases) configuration below for usage.
---
## DATABASES
!!! info "This parameter was introduced in NetBox v4.3."
NetBox requires access to a PostgreSQL 14 or later database service to store data. This service can run locally on the NetBox server or on a remote system. Databases are defined as named dictionaries:
```python
DATABASES = {
'default': {...},
'external1': {...},
'external2': {...},
}
```
NetBox itself requires only that a `default` database is defined. However, certain plugins may require the configuration of additional databases. (Consider also configuring the [`DATABASE_ROUTERS`](./system.md#database_routers) parameter when multiple databases are in use.)
The following parameters must be defined for each database:
* `NAME` - Database name * `NAME` - Database name
* `USER` - PostgreSQL username * `USER` - PostgreSQL username
@ -61,8 +38,7 @@ The following parameters must be defined for each database:
Example: Example:
```python ```python
DATABASES = { DATABASE = {
'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.postgresql',
'NAME': 'netbox', # Database name 'NAME': 'netbox', # Database name
'USER': 'netbox', # PostgreSQL username 'USER': 'netbox', # PostgreSQL username
@ -71,14 +47,13 @@ DATABASES = {
'PORT': '', # Database port (leave blank for default) 'PORT': '', # Database port (leave blank for default)
'CONN_MAX_AGE': 300, # Max database connection age 'CONN_MAX_AGE': 300, # Max database connection age
} }
}
``` ```
!!! note !!! note
NetBox supports all PostgreSQL database options supported by the underlying Django framework. For a complete list of available parameters, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/settings/#databases). NetBox supports all PostgreSQL database options supported by the underlying Django framework. For a complete list of available parameters, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/settings/#databases).
!!! warning !!! warning
The `ENGINE` parameter must specify a PostgreSQL-compatible database backend. If not defined, the default engine `django.db.backends.postgresql` will be used. Make sure to use a PostgreSQL-compatible backend for the ENGINE setting. If you don't specify an ENGINE, the default will be django.db.backends.postgresql.
--- ---

View File

@ -2,10 +2,7 @@
## ALLOW_TOKEN_RETRIEVAL ## ALLOW_TOKEN_RETRIEVAL
Default: `False` Default: `True`
!!! note
The default value of this parameter changed from `True` to `False` in NetBox v4.3.0.
If disabled, the values of API tokens will not be displayed after each token's initial creation. A user **must** record the value of a token prior to its creation, or it will be lost. Note that this affects _all_ users, regardless of assigned permissions. If disabled, the values of API tokens will not be displayed after each token's initial creation. A user **must** record the value of a token prior to its creation, or it will be lost. Note that this affects _all_ users, regardless of assigned permissions.
@ -52,7 +49,7 @@ Although it is not recommended, the default validation rules can be disabled by
Default: `False` Default: `False`
If `True`, cross-origin resource sharing (CORS) requests will be accepted from all origins. If False, a whitelist will be used (see below). If True, cross-origin resource sharing (CORS) requests will be accepted from all origins. If False, a whitelist will be used (see below).
--- ---
@ -62,7 +59,7 @@ If `True`, cross-origin resource sharing (CORS) requests will be accepted from a
These settings specify a list of origins that are authorized to make cross-site API requests. Use These settings specify a list of origins that are authorized to make cross-site API requests. Use
`CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular `CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular
expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is `True`.) For example: expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is True.) For example:
```python ```python
CORS_ORIGIN_WHITELIST = [ CORS_ORIGIN_WHITELIST = [
@ -84,7 +81,7 @@ The name of the cookie to use for the cross-site request forgery (CSRF) authenti
Default: `False` Default: `False`
If `True`, the cookie employed for cross-site request forgery (CSRF) protection will be marked as secure, meaning that it can only be sent across an HTTPS connection. If true, the cookie employed for cross-site request forgery (CSRF) protection will be marked as secure, meaning that it can only be sent across an HTTPS connection.
--- ---
@ -92,7 +89,7 @@ If `True`, the cookie employed for cross-site request forgery (CSRF) protection
Default: `[]` Default: `[]`
Defines a list of trusted origins for unsafe (e.g. `POST`) requests. This is a pass-through to Django's [`CSRF_TRUSTED_ORIGINS`](https://docs.djangoproject.com/en/stable/ref/settings/#csrf-trusted-origins) setting. Note that each host listed must specify a scheme (e.g. `http://` or `https://). Defines a list of trusted origins for unsafe (e.g. `POST`) requests. This is a pass-through to Django's [`CSRF_TRUSTED_ORIGINS`](https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS) setting. Note that each host listed must specify a scheme (e.g. `http://` or `https://).
```python ```python
CSRF_TRUSTED_ORIGINS = ( CSRF_TRUSTED_ORIGINS = (
@ -135,7 +132,7 @@ DEFAULT_PERMISSIONS = {
## EXEMPT_VIEW_PERMISSIONS ## EXEMPT_VIEW_PERMISSIONS
Default: `[]` (Empty list) Default: Empty list
A list of NetBox models to exempt from the enforcement of view permissions. Models listed here will be viewable by all users, both authenticated and anonymous. A list of NetBox models to exempt from the enforcement of view permissions. Models listed here will be viewable by all users, both authenticated and anonymous.
@ -164,7 +161,7 @@ EXEMPT_VIEW_PERMISSIONS = ['*']
Default: `False` Default: `False`
If `True`, the lifetime of a user's authentication session will be automatically reset upon each valid request. For example, if [`LOGIN_TIMEOUT`](#login_timeout) is configured to 14 days (the default), and a user whose session is due to expire in five days makes a NetBox request (with a valid session cookie), the session's lifetime will be reset to 14 days. If true, the lifetime of a user's authentication session will be automatically reset upon each valid request. For example, if [`LOGIN_TIMEOUT`](#login_timeout) is configured to 14 days (the default), and a user whose session is due to expire in five days makes a NetBox request (with a valid session cookie), the session's lifetime will be reset to 14 days.
Note that enabling this setting causes NetBox to update a user's session in the database (or file, as configured per [`SESSION_FILE_PATH`](#session_file_path)) with each request, which may introduce significant overhead in very active environments. It also permits an active user to remain authenticated to NetBox indefinitely. Note that enabling this setting causes NetBox to update a user's session in the database (or file, as configured per [`SESSION_FILE_PATH`](#session_file_path)) with each request, which may introduce significant overhead in very active environments. It also permits an active user to remain authenticated to NetBox indefinitely.
@ -189,17 +186,6 @@ The lifetime (in seconds) of the authentication cookie issued to a NetBox user u
--- ---
## LOGIN_FORM_HIDDEN
Default: `False`
Option to hide the login form when only SSO authentication is in use.
!!! warning
If the SSO provider is unreachable, login to NetBox will be impossible if this option is enabled. The only recourse is to disable it in the local configuration and restart the NetBox service.
---
## LOGOUT_REDIRECT_URL ## LOGOUT_REDIRECT_URL
Default: `'home'` Default: `'home'`
@ -212,7 +198,7 @@ The view name or URL to which a user is redirected after logging out.
Default: `False` Default: `False`
If `True`, the `includeSubDomains` directive will be included in the HTTP Strict Transport Security (HSTS) header. This directive instructs the browser to apply the HSTS policy to all subdomains of the current domain. If true, the `includeSubDomains` directive will be included in the HTTP Strict Transport Security (HSTS) header. This directive instructs the browser to apply the HSTS policy to all subdomains of the current domain.
--- ---
@ -220,7 +206,7 @@ If `True`, the `includeSubDomains` directive will be included in the HTTP Strict
Default: `False` Default: `False`
If `True`, the `preload` directive will be included in the HTTP Strict Transport Security (HSTS) header. This directive instructs the browser to preload the site in HTTPS. Browsers that use the HSTS preload list will force the site to be accessed via HTTPS even if the user types HTTP in the address bar. If true, the `preload` directive will be included in the HTTP Strict Transport Security (HSTS) header. This directive instructs the browser to preload the site in HTTPS. Browsers that use the HSTS preload list will force the site to be accessed via HTTPS even if the user types HTTP in the address bar.
--- ---
@ -236,7 +222,7 @@ If set to a non-zero integer value, the SecurityMiddleware sets the HTTP Strict
Default: `False` Default: `False`
If `True`, all non-HTTPS requests will be automatically redirected to use HTTPS. If true, all non-HTTPS requests will be automatically redirected to use HTTPS.
!!! warning !!! warning
Ensure that your frontend HTTP daemon has been configured to forward the HTTP scheme correctly before enabling this option. An incorrectly configured frontend may result in a looping redirect. Ensure that your frontend HTTP daemon has been configured to forward the HTTP scheme correctly before enabling this option. An incorrectly configured frontend may result in a looping redirect.
@ -255,7 +241,7 @@ The name used for the session cookie. See the [Django documentation](https://doc
Default: `False` Default: `False`
If `True`, the cookie employed for session authentication will be marked as secure, meaning that it can only be sent across an HTTPS connection. If true, the cookie employed for session authentication will be marked as secure, meaning that it can only be sent across an HTTPS connection.
--- ---

View File

@ -12,16 +12,6 @@ BASE_PATH = 'netbox/'
--- ---
## DATABASE_ROUTERS
!!! info "This parameter was introduced in NetBox v4.3."
Default: `[]` (empty list)
An iterable of [database routers](https://docs.djangoproject.com/en/stable/topics/db/multi-db/) to use for automatically selecting the appropriate database(s) for a query. This is useful only when [multiple databases](./required-parameters.md#databases) have been configured.
---
## DEFAULT_LANGUAGE ## DEFAULT_LANGUAGE
Default: `en-us` (US English) Default: `en-us` (US English)
@ -85,8 +75,6 @@ HTTP_PROXIES = {
} }
``` ```
If more flexibility is needed in determining which proxy to use for a given request, consider implementing one or more custom proxy routers via the [`PROXY_ROUTERS`](#proxy_routers) parameter.
--- ---
## INTERNAL_IPS ## INTERNAL_IPS
@ -95,7 +83,7 @@ Default: `('127.0.0.1', '::1')`
A list of IP addresses recognized as internal to the system, used to control the display of debugging output. For A list of IP addresses recognized as internal to the system, used to control the display of debugging output. For
example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP
addresses (and [`DEBUG`](./development.md#debug) is `True`). addresses (and [`DEBUG`](./development.md#debug) is true).
--- ---
@ -103,7 +91,7 @@ addresses (and [`DEBUG`](./development.md#debug) is `True`).
Default: `False` Default: `False`
Set this configuration parameter to `True` for NetBox deployments which do not have Internet access. This will disable miscellaneous functionality which depends on access to the Internet. Set this configuration parameter to True for NetBox deployments which do not have Internet access. This will disable miscellaneous functionality which depends on access to the Internet.
!!! note !!! note
If Internet access is available via a proxy, set [`HTTP_PROXIES`](#http_proxies) instead. If Internet access is available via a proxy, set [`HTTP_PROXIES`](#http_proxies) instead.
@ -114,7 +102,7 @@ Set this configuration parameter to `True` for NetBox deployments which do not h
Default: `{}` Default: `{}`
A dictionary of custom Jinja2 filters with the key being the filter name and the value being a callable. For more information see the [Jinja2 documentation](https://jinja.palletsprojects.com/en/3.1.x/api/#custom-filters). For example: A dictionary of custom jinja2 filters with the key being the filter name and the value being a callable. For more information see the [Jinja2 documentation](https://jinja.palletsprojects.com/en/3.1.x/api/#custom-filters). For example:
```python ```python
def uppercase(x): def uppercase(x):
@ -158,7 +146,6 @@ LOGGING = {
* `netbox.<app>.<model>` - Generic form for model-specific log messages * `netbox.<app>.<model>` - Generic form for model-specific log messages
* `netbox.auth.*` - Authentication events * `netbox.auth.*` - Authentication events
* `netbox.api.views.*` - Views which handle business logic for the REST API * `netbox.api.views.*` - Views which handle business logic for the REST API
* `netbox.event_rules` - Event rules
* `netbox.reports.*` - Report execution (`module.name`) * `netbox.reports.*` - Report execution (`module.name`)
* `netbox.scripts.*` - Custom script execution (`module.name`) * `netbox.scripts.*` - Custom script execution (`module.name`)
* `netbox.views.*` - Views which handle business logic for the web UI * `netbox.views.*` - Views which handle business logic for the web UI
@ -173,18 +160,6 @@ The file path to the location where media files (such as image attachments) are
--- ---
## PROXY_ROUTERS
!!! info "This parameter was introduced in NetBox v4.3."
Default: `["utilities.proxy.DefaultProxyRouter"]`
A list of Python classes responsible for determining which proxy server(s) to use for outbound HTTP requests. Each item in the list can be the class itself or the dotted path to the class.
The `route()` method on each class must return a dictionary of candidate proxies arranged by protocol (e.g. `http` and/or `https`), or None if no viable proxy can be determined. The default class, `DefaultProxyRouter`, simply returns the content of [`HTTP_PROXIES`](#http_proxies).
---
## REPORTS_ROOT ## REPORTS_ROOT
Default: `$INSTALL_ROOT/netbox/reports/` Default: `$INSTALL_ROOT/netbox/reports/`
@ -209,46 +184,23 @@ The dotted path to the desired search backend class. `CachedValueSearchBackend`
--- ---
## STORAGES ## STORAGE_BACKEND
The backend storage engine for handling uploaded files such as [image attachments](../models/extras/imageattachment.md) and [custom scripts](../customization/custom-scripts.md). NetBox integrates with the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) and [`django-storage-swift`](https://github.com/dennisv/django-storage-swift) libraries, which provide backends for several popular file storage services. If not configured, local filesystem storage will be used. Default: None (local storage)
By default, the following configuration is used: The backend storage engine for handling uploaded files (e.g. image attachments). NetBox supports integration with the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) and [`django-storage-swift`](https://github.com/dennisv/django-storage-swift) packages, which provide backends for several popular file storage services. If not configured, local filesystem storage will be used.
```python The configuration parameters for the specified storage backend are defined under the `STORAGE_CONFIG` setting.
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
"scripts": {
"BACKEND": "extras.storage.ScriptFileSystemStorage",
},
}
```
Within the `STORAGES` dictionary, `"default"` is used for image uploads, "staticfiles" is for static files and `"scripts"` is used for custom scripts. ---
If using a remote storage like S3, define the config as `STORAGES[key]["OPTIONS"]` for each storage item as needed. For example: ## STORAGE_CONFIG
```python Default: Empty
STORAGES = {
"scripts": {
"BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
"OPTIONS": {
'access_key': 'access key',
'secret_key': 'secret key',
}
},
}
```
The specific configuration settings for each storage backend can be found in the [django-storages documentation](https://django-storages.readthedocs.io/en/latest/index.html). A dictionary of configuration parameters for the storage backend configured as `STORAGE_BACKEND`. The specific parameters to be used here are specific to each backend; see the documentation for your selected backend ([`django-storages`](https://django-storages.readthedocs.io/en/stable/) or [`django-storage-swift`](https://github.com/dennisv/django-storage-swift)) for more detail.
!!! note If `STORAGE_BACKEND` is not defined, this setting will be ignored.
Any keys defined in the `STORAGES` configuration parameter replace those in the default configuration. It is only necessary to define keys within the `STORAGES` for the specific backend(s) you wish to configure.
--- ---

View File

@ -140,8 +140,6 @@ The Script class provides two convenience methods for reading data from files:
These two methods will load data in YAML or JSON format, respectively, from files within the local path (i.e. `SCRIPTS_ROOT`). These two methods will load data in YAML or JSON format, respectively, from files within the local path (i.e. `SCRIPTS_ROOT`).
**Note:** These convenience methods are deprecated and will be removed in NetBox v4.4. These only work if running scripts within the local path, they will not work if using a storage other than ScriptFileSystemStorage.
## Logging ## Logging
The Script object provides a set of convenient functions for recording messages at different severity levels: The Script object provides a set of convenient functions for recording messages at different severity levels:

View File

@ -76,13 +76,11 @@ Create the following for each model:
## 13. GraphQL API components ## 13. GraphQL API components
Create the following for each model: Create a GraphQL object type for the model in `graphql/types.py` by subclassing the appropriate class from `netbox.graphql.types`.
* GraphQL object type for the model in `graphql/types.py` (subclass the appropriate class from `netbox.graphql.types`) **Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["policy"])` or similar.
* Add a GraphQL filter for the model in `graphql/filters.py`
* Extend the query class for the app in `graphql/schema.py` with the individual object and object list fields
**Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["foo"])` or similar. Also extend the schema class defined in `graphql/schema.py` with the individual object and object list fields per the established convention.
## 14. Add tests ## 14. Add tests

View File

@ -6,7 +6,7 @@ Below is a list of tasks to consider when adding a new field to a core model.
Add the field to the model, taking care to address any of the following conditions. Add the field to the model, taking care to address any of the following conditions.
* When adding a GenericForeignKey field, you may need add an index under `Meta` for its two concrete fields. (This is required only for non-unique GFK relationships, as the unique constraint introduces its own index.) For example: * When adding a GenericForeignKey field, also add an index under `Meta` for its two concrete fields. For example:
```python ```python
class Meta: class Meta:

View File

@ -115,7 +115,7 @@ You may also need to set up the yarn packages as shown in the [Web UI Developmen
Within the `netbox/netbox/` directory, copy `configuration_example.py` to `configuration.py` and update the following parameters: Within the `netbox/netbox/` directory, copy `configuration_example.py` to `configuration.py` and update the following parameters:
* `ALLOWED_HOSTS`: This can be set to `['*']` for development purposes * `ALLOWED_HOSTS`: This can be set to `['*']` for development purposes
* `DATABASES`: PostgreSQL database connection parameters * `DATABASE`: PostgreSQL database connection parameters
* `REDIS`: Redis configuration (if different from the defaults) * `REDIS`: Redis configuration (if different from the defaults)
* `SECRET_KEY`: Set to a random string (use `generate_secret_key.py` in the parent directory to generate a suitable key) * `SECRET_KEY`: Set to a random string (use `generate_secret_key.py` in the parent directory to generate a suitable key)
* `DEBUG`: Set to `True` * `DEBUG`: Set to `True`
@ -147,7 +147,7 @@ For UI development you will need to review the [Web UI Development Guide](web-ui
## Populating Demo Data ## Populating Demo Data
Once you have your development environment up and running, it might be helpful to populate some "dummy" data to make interacting with the UI and APIs more convenient. Check out the [netbox-demo-data](https://github.com/netbox-community/netbox-demo-data) repo on GitHub, which houses a collection of sample data that can be easily imported to any new NetBox deployment. This sample data is used to populate the [public demo instance](https://demo.netbox.dev). Once you have your development environment up and running, it might be helpful to populate some "dummy" data to make interacting with the UI and APIs more convenient. Check out the [netbox-demo-data](https://github.com/netbox-community/netbox-demo-data) repo on GitHub, which houses a collection of sample data that can be easily imported to any new NetBox deployment. (This sample data is used to populate the public demo instance at <https://demo.netbox.dev>.)
The demo data is provided in JSON format and loaded into an empty database using Django's `loaddata` management command. Consult the demo data repo's `README` file for complete instructions on populating the data. The demo data is provided in JSON format and loaded into an empty database using Django's `loaddata` management command. Consult the demo data repo's `README` file for complete instructions on populating the data.

View File

@ -53,8 +53,6 @@ If a new Django release is adopted or other major dependencies (Python, PostgreS
* Update the installation guide (`docs/installation/index.md`) with the new minimum versions. * Update the installation guide (`docs/installation/index.md`) with the new minimum versions.
* Update the upgrade guide (`docs/installation/upgrading.md`) for the current version accordingly. * Update the upgrade guide (`docs/installation/upgrading.md`) for the current version accordingly.
* Update the minimum PostgreSQL version in the programming error template (`netbox/templates/exceptions/programming_error.html`).
* Update the minimum and supported Python versions in the project metadata file (`pyproject.toml`)
### Manually Perform a New Install ### Manually Perform a New Install
@ -166,8 +164,7 @@ Then, compile these portable (`.po`) files for use in the application:
### Update Version and Changelog ### Update Version and Changelog
* Update the version number and published date in `netbox/release.yaml`. Add or remove the designation (e.g. `beta1`) if applicable. * Update the version number and date in `netbox/release.yaml`. Add or remove the designation (e.g. `beta1`) if applicable.
* Copy the version number from `release.yaml` to `pyproject.toml` in the project root.
* Update the example version numbers in the feature request and bug report templates under `.github/ISSUE_TEMPLATES/`. * Update the example version numbers in the feature request and bug report templates under `.github/ISSUE_TEMPLATES/`.
* Add a section for this release at the top of the changelog page for the minor version (e.g. `docs/release-notes/version-4.2.md`) listing all relevant changes made in this release. * Add a section for this release at the top of the changelog page for the minor version (e.g. `docs/release-notes/version-4.2.md`) listing all relevant changes made in this release.
@ -193,3 +190,15 @@ Create a [new release](https://github.com/netbox-community/netbox/releases/new)
* **Description:** Copy from the pull request body, then promote the `###` headers to `##` ones * **Description:** Copy from the pull request body, then promote the `###` headers to `##` ones
Once created, the release will become available for users to install. Once created, the release will become available for users to install.
### Update the Public Documentation
After a release has been published, the public NetBox documentation needs to be updated. This is accomplished by running two actions on the [netboxlabs-docs](https://github.com/netboxlabs/netboxlabs-docs) repository.
First, run the `build-site` action, by navigating to Actions > build-site > Run workflow. This process compiles the documentation along with an overlay for integration with the documentation portal at <https://netboxlabs.com/docs>. The job should take about two minutes.
Once the documentation files have been compiled, they must be published by running the `deploy-kinsta` action. Select the desired deployment environment (staging or production) and specify `latest` as the deploy tag.
Clear the CDN cache from the [Kinsta](https://my.kinsta.com/) portal. Navigate to _Sites_ / _NetBox Labs_ / _Live_, select _Cache_ in the left-nav, click the _Clear Cache_ button, and confirm the clear operation.
Finally, verify that the documentation at <https://netboxlabs.com/docs/netbox/en/stable/> has been updated.

View File

@ -2,9 +2,9 @@
NetBox includes the ability to execute certain functions as background tasks. These include: NetBox includes the ability to execute certain functions as background tasks. These include:
* [Report](../customization/reports.md) execution
* [Custom script](../customization/custom-scripts.md) execution * [Custom script](../customization/custom-scripts.md) execution
* Synchronization of [remote data sources](../integrations/synchronized-data.md) * Synchronization of [remote data sources](../integrations/synchronized-data.md)
* Housekeeping tasks
Additionally, NetBox plugins can enqueue their own background tasks. This is accomplished using the [Job model](../models/core/job.md). Background tasks are executed by the `rqworker` process(es). Additionally, NetBox plugins can enqueue their own background tasks. This is accomplished using the [Job model](../models/core/job.md). Background tasks are executed by the `rqworker` process(es).

View File

@ -9,7 +9,7 @@ NetBox is the leading solution for modeling and documenting modern networks. By
## :material-server-network: Built for Networks ## :material-server-network: Built for Networks
Unlike general-purpose configuration management databases (CMDBs), NetBox has curated a data model which caters specifically to the needs of network engineers and operators. It delivers a wide assortment of object types carefully crafted to best serve the needs of infrastructure design and documentation. These cover all facets of network technology, from IP address managements to cabling to overlays and more: Unlike general-purpose CMDBs, NetBox has curated a data model which caters specifically to the needs of network engineers and operators. It delivers a wide assortment of object types carefully crafted to best serve the needs of infrastructure design and documentation. These cover all facets of network technology, from IP address managements to cabling to overlays and more:
* Hierarchical regions, sites, and locations * Hierarchical regions, sites, and locations
* Racks, devices, and device components * Racks, devices, and device components

View File

@ -2,17 +2,39 @@
This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md). This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md).
!!! warning "PostgreSQL 14 or later required" !!! warning "PostgreSQL 13 or later required"
NetBox requires PostgreSQL 14 or later. Please note that MySQL and other relational databases are **not** supported. NetBox requires PostgreSQL 13 or later. Please note that MySQL and other relational databases are **not** supported.
## Installation ## Installation
=== "Ubuntu"
```no-highlight ```no-highlight
sudo apt update sudo apt update
sudo apt install -y postgresql sudo apt install -y postgresql
``` ```
Before continuing, verify that you have installed PostgreSQL 14 or later: === "CentOS"
```no-highlight
sudo yum install -y postgresql-server
sudo postgresql-setup --initdb
```
CentOS configures ident host-based authentication for PostgreSQL by default. Because NetBox will need to authenticate using a username and password, modify `/var/lib/pgsql/data/pg_hba.conf` to support MD5 authentication by changing `ident` to `md5` for the lines below:
```no-highlight
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
```
Once PostgreSQL has been installed, start the service and enable it to run at boot:
```no-highlight
sudo systemctl enable --now postgresql
```
Before continuing, verify that you have installed PostgreSQL 13 or later:
```no-highlight ```no-highlight
psql -V psql -V

View File

@ -4,10 +4,19 @@
[Redis](https://redis.io/) is an in-memory key-value store which NetBox employs for caching and queuing. This section entails the installation and configuration of a local Redis instance. If you already have a Redis service in place, skip to [the next section](3-netbox.md). [Redis](https://redis.io/) is an in-memory key-value store which NetBox employs for caching and queuing. This section entails the installation and configuration of a local Redis instance. If you already have a Redis service in place, skip to [the next section](3-netbox.md).
=== "Ubuntu"
```no-highlight ```no-highlight
sudo apt install -y redis-server sudo apt install -y redis-server
``` ```
=== "CentOS"
```no-highlight
sudo yum install -y redis
sudo systemctl enable --now redis
```
Before continuing, verify that your installed version of Redis is at least v4.0: Before continuing, verify that your installed version of Redis is at least v4.0:
```no-highlight ```no-highlight

View File

@ -9,10 +9,16 @@ Begin by installing all system packages required by NetBox and its dependencies.
!!! warning "Python 3.10 or later required" !!! warning "Python 3.10 or later required"
NetBox supports Python 3.10, 3.11, and 3.12. NetBox supports Python 3.10, 3.11, and 3.12.
=== "Ubuntu"
```no-highlight ```no-highlight
sudo apt install -y python3 python3-pip python3-venv python3-dev \ sudo apt install -y python3 python3-pip python3-venv python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev
build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev \ ```
libssl-dev zlib1g-dev
=== "CentOS"
```no-highlight
sudo yum install -y gcc libxml2-devel libxslt-devel libffi-devel libpq-devel openssl-devel redhat-rpm-config
``` ```
Before continuing, check that your installed Python version is at least 3.10: Before continuing, check that your installed Python version is at least 3.10:
@ -49,10 +55,18 @@ cd /opt/netbox/
If `git` is not already installed, install it: If `git` is not already installed, install it:
=== "Ubuntu"
```no-highlight ```no-highlight
sudo apt install -y git sudo apt install -y git
``` ```
=== "CentOS"
```no-highlight
sudo yum install -y git
```
Next, clone the git repository: Next, clone the git repository:
```no-highlight ```no-highlight
@ -83,6 +97,8 @@ Using this installation method enables easy upgrades in the future by simply che
Create a system user account named `netbox`. We'll configure the WSGI and HTTP services to run under this account. We'll also assign this user ownership of the media directory. This ensures that NetBox will be able to save uploaded files. Create a system user account named `netbox`. We'll configure the WSGI and HTTP services to run under this account. We'll also assign this user ownership of the media directory. This ensures that NetBox will be able to save uploaded files.
=== "Ubuntu"
``` ```
sudo adduser --system --group netbox sudo adduser --system --group netbox
sudo chown --recursive netbox /opt/netbox/netbox/media/ sudo chown --recursive netbox /opt/netbox/netbox/media/
@ -90,6 +106,16 @@ sudo chown --recursive netbox /opt/netbox/netbox/reports/
sudo chown --recursive netbox /opt/netbox/netbox/scripts/ sudo chown --recursive netbox /opt/netbox/netbox/scripts/
``` ```
=== "CentOS"
```
sudo groupadd --system netbox
sudo adduser --system -g netbox netbox
sudo chown --recursive netbox /opt/netbox/netbox/media/
sudo chown --recursive netbox /opt/netbox/netbox/reports/
sudo chown --recursive netbox /opt/netbox/netbox/scripts/
```
## Configuration ## Configuration
Move into the NetBox configuration directory and make a copy of `configuration_example.py` named `configuration.py`. This file will hold all of your local configuration parameters. Move into the NetBox configuration directory and make a copy of `configuration_example.py` named `configuration.py`. This file will hold all of your local configuration parameters.
@ -102,13 +128,13 @@ sudo cp configuration_example.py configuration.py
Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](../configuration/index.md), but only the following four are required for new installations: Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](../configuration/index.md), but only the following four are required for new installations:
* `ALLOWED_HOSTS` * `ALLOWED_HOSTS`
* `DATABASES` (or `DATABASE`) * `DATABASE`
* `REDIS` * `REDIS`
* `SECRET_KEY` * `SECRET_KEY`
### ALLOWED_HOSTS ### ALLOWED_HOSTS
This is a list of the valid hostnames and IP addresses by which this server can be reached. You must specify at least one name or IP address. (Note that this does not restrict the locations from which NetBox may be accessed: It is merely for [HTTP host header validation](https://docs.djangoproject.com/en/stable/topics/security/#host-headers-virtual-hosting).) This is a list of the valid hostnames and IP addresses by which this server can be reached. You must specify at least one name or IP address. (Note that this does not restrict the locations from which NetBox may be accessed: It is merely for [HTTP host header validation](https://docs.djangoproject.com/en/3.0/topics/security/#host-headers-virtual-hosting).)
```python ```python
ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123'] ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
@ -120,15 +146,12 @@ If you are not yet sure what the domain name and/or IP address of the NetBox ins
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
``` ```
### DATABASES ### DATABASE
This parameter holds the PostgreSQL database configuration details. The default database must be defined; additional databases may be defined as needed e.g. by plugins. This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, update the `HOST` and `PORT` parameters accordingly. See the [configuration documentation](../configuration/required-parameters.md#database) for more detail on individual parameters.
A username and password must be defined for the default database. If the service is running on a remote host, update the `HOST` and `PORT` parameters accordingly. See the [configuration documentation](../configuration/required-parameters.md#databases) for more detail on individual parameters.
```python ```python
DATABASES = { DATABASE = {
'default': {
'NAME': 'netbox', # Database name 'NAME': 'netbox', # Database name
'USER': 'netbox', # PostgreSQL username 'USER': 'netbox', # PostgreSQL username
'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
@ -136,7 +159,6 @@ DATABASES = {
'PORT': '', # Database port (leave blank for default) 'PORT': '', # Database port (leave blank for default)
'CONN_MAX_AGE': 300, # Max database connection age (seconds) 'CONN_MAX_AGE': 300, # Max database connection age (seconds)
} }
}
``` ```
### REDIS ### REDIS
@ -185,7 +207,7 @@ All Python packages required by NetBox are listed in `requirements.txt` and will
### Remote File Storage ### Remote File Storage
By default, NetBox will use the local filesystem to store uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired storage backend](../configuration/system.md#storages) in `configuration.py`. By default, NetBox will use the local filesystem to store uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired storage backend](../configuration/system.md#storage_backend) in `configuration.py`.
```no-highlight ```no-highlight
sudo sh -c "echo 'django-storages' >> /opt/netbox/local_requirements.txt" sudo sh -c "echo 'django-storages' >> /opt/netbox/local_requirements.txt"

View File

@ -28,7 +28,7 @@ NetBox ships with a default configuration file for uWSGI. To use it, copy `/opt/
sudo cp /opt/netbox/contrib/uwsgi.ini /opt/netbox/uwsgi.ini sudo cp /opt/netbox/contrib/uwsgi.ini /opt/netbox/uwsgi.ini
``` ```
While the provided configuration should suffice for most initial installations, you may wish to edit this file to change the bound IP address and/or port number, or to make performance-related adjustments. See [the uWSGI documentation](https://uwsgi-docs-additions.readthedocs.io/en/latest/Options.html) for the available configuration parameters and take a minute to review the [Things to know](https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html) page. Django also provides [additional documentation](https://docs.djangoproject.com/en/stable/howto/deployment/wsgi/uwsgi/) on configuring uWSGI with a Django app. While the provided configuration should suffice for most initial installations, you may wish to edit this file to change the bound IP address and/or port number, or to make performance-related adjustments. See [the uWSGI documentation](https://uwsgi-docs-additions.readthedocs.io/en/latest/Options.html) for the available configuration parameters and take a minute to review the [Things to know](https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html) page. Django also provides [additional documentation](https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/uwsgi/) on configuring uWSGI with a Django app.
## systemd Setup ## systemd Setup

View File

@ -6,10 +6,18 @@ This guide explains how to implement LDAP authentication using an external serve
### Install System Packages ### Install System Packages
On Ubuntu:
```no-highlight ```no-highlight
sudo apt install -y libldap2-dev libsasl2-dev libssl-dev sudo apt install -y libldap2-dev libsasl2-dev libssl-dev
``` ```
On CentOS:
```no-highlight
sudo yum install -y openldap-devel python3-devel
```
### Install django-auth-ldap ### Install django-auth-ldap
Activate the Python virtual environment and install the `django-auth-ldap` package using pip: Activate the Python virtual environment and install the `django-auth-ldap` package using pip:

View File

@ -1,18 +1,9 @@
# Installation # Installation
<div class="grid cards" markdown> !!! info "NetBox Cloud"
The instructions below are for installing NetBox as a standalone, self-hosted application. For a Cloud-delivered solution, check out [NetBox Cloud](https://netboxlabs.com/netbox-cloud/) by NetBox Labs.
- :material-clock-fast:{ .lg .middle } __Eager to Get Started?__ The installation instructions provided here have been tested to work on Ubuntu 22.04 and CentOS 8.3. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
---
Check out the [NetBox Cloud Free Plan](https://netboxlabs.com/free-netbox-cloud/)! Skip the installation process and grab your own NetBox Cloud instance, preconfigured and ready to go in minutes. Completely free!
[:octicons-arrow-right-24: Sign Up](https://signup.netboxlabs.com/)
</div>
The installation instructions provided here have been tested to work on Ubuntu 22.04. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors.
The following sections detail how to set up a new instance of NetBox: The following sections detail how to set up a new instance of NetBox:
@ -28,7 +19,7 @@ The following sections detail how to set up a new instance of NetBox:
| Dependency | Supported Versions | | Dependency | Supported Versions |
|------------|--------------------| |------------|--------------------|
| Python | 3.10, 3.11, 3.12 | | Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 14+ | | PostgreSQL | 13+ |
| Redis | 4.0+ | | Redis | 4.0+ |
Below is a simplified overview of the NetBox application stack for reference: Below is a simplified overview of the NetBox application stack for reference:

View File

@ -17,51 +17,53 @@ Prior to upgrading your NetBox instance, be sure to carefully review all [releas
NetBox requires the following dependencies: NetBox requires the following dependencies:
=== "Current Version"
| Dependency | Supported Versions | | Dependency | Supported Versions |
|------------|--------------------| |------------|--------------------|
| Python | 3.10, 3.11, 3.12 | | Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 14+ | | PostgreSQL | 13+ |
| Redis | 4.0+ | | Redis | 4.0+ |
### Version History === "All Versions"
| NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation | | NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation |
|:--------------:|:----------:|:----------:|:--------------:|:---------:|:-------------------------------------------------------------------------------------------------:| |:--------------:|:----------:|:----------:|:--------------:|:---------:|:-------------------------------------------------------------------------------------------------:|
| 4.3 | 3.10 | 3.12 | 14 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.3.0/docs/installation/index.md) | | 4.2 | 3.10 | 3.12 | **13** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.2.0/docs/installation/index.md) |
| 4.2 | 3.10 | 3.12 | 13 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.2.0/docs/installation/index.md) |
| 4.1 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.1.0/docs/installation/index.md) | | 4.1 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.1.0/docs/installation/index.md) |
| 4.0 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.0.0/docs/installation/index.md) | | 4.0 | **3.10** | **3.12** | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.0.0/docs/installation/index.md) |
| 3.7 | 3.8 | 3.11 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.7.0/docs/installation/index.md) | | 3.7 | 3.8 | 3.11 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.7.0/docs/installation/index.md) |
| 3.6 | 3.8 | 3.11 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.6.0/docs/installation/index.md) | | 3.6 | 3.8 | **3.11** | **12** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.6.0/docs/installation/index.md) |
| 3.5 | 3.8 | 3.10 | 11 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.5.0/docs/installation/index.md) | | 3.5 | 3.8 | 3.10 | 11 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.5.0/docs/installation/index.md) |
| 3.4 | 3.8 | 3.10 | 11 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.4.0/docs/installation/index.md) | | 3.4 | 3.8 | 3.10 | **11** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.4.0/docs/installation/index.md) |
| 3.3 | 3.8 | 3.10 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.3.0/docs/installation/index.md) | | 3.3 | 3.8 | 3.10 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.3.0/docs/installation/index.md) |
| 3.2 | 3.8 | 3.10 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.2.0/docs/installation/index.md) | | 3.2 | **3.8** | **3.10** | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.2.0/docs/installation/index.md) |
| 3.1 | 3.7 | 3.9 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.1.0/docs/installation/index.md) | | 3.1 | 3.7 | 3.9 | **10** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.1.0/docs/installation/index.md) |
| 3.0 | 3.7 | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.0.0/docs/installation/index.md) | | 3.0 | **3.7** | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.0.0/docs/installation/index.md) |
| 2.11 | 3.6 | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.11.0/docs/installation/index.md) | | 2.11 | 3.6 | **3.9** | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.11.0/docs/installation/index.md) |
| 2.10 | 3.6 | 3.8 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.10.0/docs/installation/index.md) | | 2.10 | 3.6 | 3.8 | **9.6** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.10.0/docs/installation/index.md) |
| 2.9 | 3.6 | 3.8 | 9.5 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.9.0/docs/installation/index.md) | | 2.9 | 3.6 | 3.8 | 9.5 | **4.0** | [Link](https://github.com/netbox-community/netbox/blob/v2.9.0/docs/installation/index.md) |
| 2.8 | 3.6 | 3.8 | 9.5 | 3.4 | [Link](https://github.com/netbox-community/netbox/blob/v2.8.0/docs/installation/index.md) | | 2.8 | **3.6** | **3.8** | **9.5** | **3.4** | [Link](https://github.com/netbox-community/netbox/blob/v2.8.0/docs/installation/index.md) |
| 2.7 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.7.0/docs/installation/index.md) | | 2.7 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.7.0/docs/installation/index.md) |
| 2.6 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.6.0/docs/installation/index.md) | | 2.6 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.6.0/docs/installation/index.md) |
| 2.5 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.5.0/docs/installation/index.md) | | 2.5 | **3.5** | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.5.0/docs/installation/index.md) |
| 2.4 | 3.4 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.4.0/docs/installation/index.md) | | 2.4 | **3.4** | **3.7** | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.4.0/docs/installation/index.md) |
| 2.3 | 2.7 | 3.6 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.3.0/docs/installation/postgresql.md) | | 2.3 | 2.7 | 3.6 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.3.0/docs/installation/postgresql.md) |
| 2.2 | 2.7 | 3.6 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.2.0/docs/installation/postgresql.md) | | 2.2 | 2.7 | 3.6 | **9.4** | - | [Link](https://github.com/netbox-community/netbox/blob/v2.2.0/docs/installation/postgresql.md) |
| 2.1 | 2.7 | 3.6 | 9.3 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.1.0/docs/installation/postgresql.md) | | 2.1 | 2.7 | 3.6 | 9.3 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.1.0/docs/installation/postgresql.md) |
| 2.0 | 2.7 | 3.6 | 9.3 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.0.0/docs/installation/postgresql.md) | | 2.0 | 2.7 | **3.6** | **9.3** | - | [Link](https://github.com/netbox-community/netbox/blob/v2.0.0/docs/installation/postgresql.md) |
| 1.9 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.9.0-r1/docs/installation/postgresql.md) | | 1.9 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.9.0-r1/docs/installation/postgresql.md) |
| 1.8 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.8.0/docs/installation/postgresql.md) | | 1.8 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.8.0/docs/installation/postgresql.md) |
| 1.7 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.7.0/docs/installation/postgresql.md) | | 1.7 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.7.0/docs/installation/postgresql.md) |
| 1.6 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.6.0/docs/installation/postgresql.md) | | 1.6 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.6.0/docs/installation/postgresql.md) |
| 1.5 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.5.0/docs/installation/postgresql.md) | | 1.5 | 2.7 | 3.5 | **9.2** | - | [Link](https://github.com/netbox-community/netbox/blob/v1.5.0/docs/installation/postgresql.md) |
| 1.4 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.4.0/docs/installation/postgresql.md) | | 1.4 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.4.0/docs/installation/postgresql.md) |
| 1.3 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.3.0/docs/installation/postgresql.md) | | 1.3 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.3.0/docs/installation/postgresql.md) |
| 1.2 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.2.0/docs/installation/postgresql.md) | | 1.2 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.2.0/docs/installation/postgresql.md) |
| 1.1 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.1.0/docs/getting-started.md) | | 1.1 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.1.0/docs/getting-started.md) |
| 1.0 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/1.0.0/docs/getting-started.md) | | 1.0 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/1.0.0/docs/getting-started.md) |
## 3. Install the Latest Release ## 3. Install the Latest Release
As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by checking out the latest production release from the git repository. As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by checking out the latest production release from the git repository.
@ -122,7 +124,7 @@ sudo cp /opt/netbox-$OLDVER/gunicorn.py /opt/netbox/
### Option B: Check Out a Git Release ### Option B: Check Out a Git Release
This guide assumes that NetBox is installed in `/opt/netbox`. First, determine the latest release either by visiting our [releases page](https://github.com/netbox-community/netbox/releases) or by running the following command: This guide assumes that NetBox is installed at `/opt/netbox`. First, determine the latest release either by visiting our [releases page](https://github.com/netbox-community/netbox/releases) or by running the following command:
``` ```
git ls-remote --tags https://github.com/netbox-community/netbox.git \ git ls-remote --tags https://github.com/netbox-community/netbox.git \
@ -134,8 +136,6 @@ git ls-remote --tags https://github.com/netbox-community/netbox.git \
Check out the desired release by specifying its tag. For example: Check out the desired release by specifying its tag. For example:
``` ```
cd /opt/netbox && \
sudo git fetch --tags && \
sudo git checkout v4.2.7 sudo git checkout v4.2.7
``` ```

View File

@ -11,7 +11,7 @@ curl -H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "Accept: application/json" \ -H "Accept: application/json" \
http://netbox/graphql/ \ http://netbox/graphql/ \
--data '{"query": "query {circuit_list(filters:{status: STATUS_ACTIVE}) {cid provider {name}}}"}' --data '{"query": "query {circuit_list(status:\"active\") {cid provider {name}}}"}'
``` ```
The response will include the requested data formatted as JSON: The response will include the requested data formatted as JSON:
@ -51,48 +51,20 @@ For more detail on constructing GraphQL queries, see the [GraphQL queries docume
## Filtering ## Filtering
!!! note "Changed in NetBox v4.3" The GraphQL API employs the same filtering logic as the UI and REST API. Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only sites within the North Carolina region with a status of active:
The filtering syntax fo the GraphQL API has changed substantially in NetBox v4.3.
Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only active sites:
``` ```
query { query {
site_list( site_list(filters: {region: "us-nc", status: "active"}) {
filters: {
status: STATUS_ACTIVE
}
) {
name name
} }
} }
``` ```
Filters can be combined with logical operators, such as `OR` and `NOT`. For example, the following will return every site that is planned _or_ assigned to a tenant named Foo: In addition, filtering can be done on list of related objects as shown in the following query:
``` ```
query { {
site_list(
filters: {
status: STATUS_PLANNED,
OR: {
tenant: {
name: {
exact: "Foo"
}
}
}
}
) {
name
}
}
```
Filtering can also be applied to related objects. For example, the following query will return only enabled interfaces for each device:
```
query {
device_list { device_list {
id id
name name
@ -131,18 +103,6 @@ Certain queries can return multiple types of objects, for example cable terminat
The field "class_type" is an easy way to distinguish what type of object it is when viewing the returned data, or when filtering. It contains the class name, for example "CircuitTermination" or "ConsoleServerPort". The field "class_type" is an easy way to distinguish what type of object it is when viewing the returned data, or when filtering. It contains the class name, for example "CircuitTermination" or "ConsoleServerPort".
## Pagination
Queries can be paginated by specifying pagination in the query and supplying an offset and optionaly a limit in the query. If no limit is given, a default of 100 is used. Queries are not paginated unless requested in the query. An example paginated query is shown below:
```
query {
device_list(pagination: { offset: 0, limit: 20 }) {
id
}
}
```
## Authentication ## Authentication
NetBox's GraphQL API uses the same API authentication tokens as its REST API. Authentication tokens are included with requests by attaching an `Authorization` HTTP header in the following form: NetBox's GraphQL API uses the same API authentication tokens as its REST API. Authentication tokens are included with requests by attaching an `Authorization` HTTP header in the following form:

View File

@ -79,5 +79,5 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and
| HTTP service | nginx or Apache | | HTTP service | nginx or Apache |
| WSGI service | gunicorn or uWSGI | | WSGI service | gunicorn or uWSGI |
| Application | Django/Python | | Application | Django/Python |
| Database | PostgreSQL 14+ | | Database | PostgreSQL 13+ |
| Task queuing | Redis/django-rq | | Task queuing | Redis/django-rq |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -44,12 +44,6 @@ A set of rules (one per line) identifying filenames to ignore during synchroniza
| `*.txt` | Ignore any files with a `.txt` extension | | `*.txt` | Ignore any files with a `.txt` extension |
| `data???.json` | Ignore e.g. `data123.json` | | `data???.json` | Ignore e.g. `data123.json` |
### Sync Interval
!!! info "This field was introduced in NetBox v4.3."
The interval at which the data source should automatically synchronize. If not set, the data source must be synchronized manually.
### Last Synced ### Last Synced
The date and time at which the source was most recently synchronized successfully. The date and time at which the source was most recently synchronized successfully.

View File

@ -4,12 +4,6 @@ Devices can be organized by functional roles, which are fully customizable by th
## Fields ## Fields
### Parent
!!! info "This field was introduced in NetBox v4.3."
The parent role of which this role is a child (optional).
### Name ### Name
A unique human-friendly name. A unique human-friendly name.

View File

@ -1,8 +1,5 @@
# Inventory Items # Inventory Items
!!! warning "Deprecation Warning"
Beginning in NetBox v4.3, the use of inventory items has been deprecated. They are planned for removal in a future NetBox release. Users are strongly encouraged to begin using [modules](./module.md) and [module types](./moduletype.md) in place of inventory items. Modules provide enhanced functionality and can be configured with user-defined attributes.
Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. They are intended to be used primarily for inventory purposes. Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. They are intended to be used primarily for inventory purposes.
Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device. An inventory item may also be associated with a specific component within the same device. For example, you may wish to associate a transceiver with an interface. Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device. An inventory item may also be associated with a specific component within the same device. For example, you may wish to associate a transceiver with an interface.

View File

@ -1,8 +1,5 @@
# Inventory Item Roles # Inventory Item Roles
!!! warning "Deprecation Warning"
Beginning in NetBox v4.3, the use of inventory items has been deprecated. They are planned for removal in a future NetBox release. Users are strongly encouraged to begin using [modules](./module.md) and [module types](./moduletype.md) in place of inventory items. Modules provide enhanced functionality and can be configured with user-defined attributes.
Inventory items can be organized by functional roles, which are fully customizable by the user. For example, you might create roles for power supplies, fans, interface optics, etc. Inventory items can be organized by functional roles, which are fully customizable by the user. For example, you might create roles for power supplies, fans, interface optics, etc.
## Fields ## Fields

View File

@ -1,6 +1,3 @@
# Inventory Item Templates # Inventory Item Templates
!!! warning "Deprecation Warning"
Beginning in NetBox v4.3, the use of inventory items has been deprecated. They are planned for removal in a future NetBox release. Users are strongly encouraged to begin using [modules](./module.md) and [module types](./moduletype.md) in place of inventory items. Modules provide enhanced functionality and can be configured with user-defined attributes.
A template for an inventory item that will be automatically created when instantiating a new device. All attributes of this object will be copied to the new inventory item, including the associations with a parent item and assigned component, if any. See the [inventory item](./inventoryitem.md) documentation for more detail. A template for an inventory item that will be automatically created when instantiating a new device. All attributes of this object will be copied to the new inventory item, including the associations with a parent item and assigned component, if any. See the [inventory item](./inventoryitem.md) documentation for more detail.

View File

@ -43,11 +43,3 @@ The numeric weight of the module, including a unit designation (e.g. 3 kilograms
### Airflow ### Airflow
The direction in which air circulates through the device chassis for cooling. The direction in which air circulates through the device chassis for cooling.
### Profile
The assigned [profile](./moduletypeprofile.md) for the type of module. Profiles can be used to classify module types by function (e.g. power supply, hard disk, etc.), and they support the addition of user-configurable attributes on module types. The assignment of a module type to a profile is optional.
### Attributes
Depending on the module type's assigned [profile](./moduletypeprofile.md) (if any), one or more user-defined attributes may be available to configure.

View File

@ -1,40 +0,0 @@
# Module Type Profiles
!!! info "This model was introduced in NetBox v4.3."
Each [module type](./moduletype.md) may optionally be assigned a profile according to its classification. A profile can extend module types with user-configured attributes. For example, you might want to specify the input current and voltage of a power supply, or the clock speed and number of cores for a processor.
Module type attributes are managed via the configuration of a [JSON schema](https://json-schema.org/) on the profile. For example, the following schema introduces three module type attributes, two of which are designated as required attributes.
```json
{
"properties": {
"type": {
"type": "string",
"title": "Disk type",
"enum": ["HD", "SSD", "NVME"],
"default": "HD"
},
"capacity": {
"type": "integer",
"title": "Capacity (GB)",
"description": "Gross disk size"
},
"speed": {
"type": "integer",
"title": "Speed (RPM)"
}
},
"required": [
"type", "capacity"
]
}
```
The assignment of module types to a profile is optional. The designation of a schema for a profile is also optional: A profile can be used simply as a mechanism for classifying module types if the addition of custom attributes is not needed.
## Fields
### Schema
This field holds the [JSON schema](https://json-schema.org/) for the profile. The configured JSON schema must be valid (or the field must be null).

View File

@ -29,19 +29,6 @@ An alternative physical label identifying the power outlet.
The type of power outlet. The type of power outlet.
### Status
The operational status of the power outlet. By default, the following statuses are available:
* Enabled
* Disabled
* Faulty
!!! tip "Custom power outlet statuses"
Additional power outlet statuses may be defined by setting `PowerOutlet.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
!!! info "This field was introduced in NetBox v4.3."
### Color ### Color
!!! info "This field was introduced in NetBox v4.2." !!! info "This field was introduced in NetBox v4.2."

View File

@ -40,9 +40,7 @@ The number of the numerically lowest unit in the rack. This value defaults to on
### Outer Dimensions ### Outer Dimensions
The external width, height and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches. The external width and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches.
!!! info "The `outer_height` field was introduced in NetBox v4.3."
### Mounting Depth ### Mounting Depth

View File

@ -0,0 +1,16 @@
# Branches
!!! danger "Deprecated Feature"
This feature has been deprecated in NetBox v4.2 and will be removed in a future release. Please consider using the [netbox-branching plugin](https://github.com/netboxlabs/netbox-branching), which provides much more robust functionality.
A branch is a collection of related [staged changes](./stagedchange.md) that have been prepared for merging into the active database. A branch can be merged by executing its `commit()` method. Deleting a branch will delete all its related changes.
## Fields
### Name
The branch's name.
### User
The user to which the branch belongs (optional).

View File

@ -12,6 +12,10 @@ See the [configuration rendering documentation](../../features/configuration-ren
A unique human-friendly name. A unique human-friendly name.
### Weight
A numeric value which influences the order in which context data is merged. Contexts with a lower weight are merged before those with a higher weight.
### Data File ### Data File
Template code may optionally be sourced from a remote [data file](../core/datafile.md), which is synchronized from a remote data source. When designating a data file, there is no need to specify template code: It will be populated automatically from the data file. Template code may optionally be sourced from a remote [data file](../core/datafile.md), which is synchronized from a remote data source. When designating a data file, there is no need to specify template code: It will be populated automatically from the data file.
@ -23,27 +27,3 @@ Jinja2 template code, if being defined locally rather than replicated from a dat
### Environment Parameters ### Environment Parameters
A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior. A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior.
### MIME Type
!!! info "This field was introduced in NetBox v4.3."
The MIME type to indicate in the response when rendering the configuration template (optional). Defaults to `text/plain`.
### File Name
!!! info "This field was introduced in NetBox v4.3."
The file name to give to the rendered export file (optional).
### File Extension
!!! info "This field was introduced in NetBox v4.3."
The file extension to append to the file name in the response (optional).
### As Attachment
!!! info "This field was introduced in NetBox v4.3."
If selected, the rendered content will be returned as a file attachment, rather than displayed directly in-browser (where supported).

View File

@ -20,20 +20,10 @@ Template code may optionally be sourced from a remote [data file](../core/datafi
Jinja2 template code for rendering the exported data. Jinja2 template code for rendering the exported data.
### Environment Parameters
!!! info "This field was introduced in NetBox v4.3."
A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior.
### MIME Type ### MIME Type
The MIME type to indicate in the response when rendering the export template (optional). Defaults to `text/plain`. The MIME type to indicate in the response when rendering the export template (optional). Defaults to `text/plain`.
### File Name
The file name to give to the rendered export file (optional).
### File Extension ### File Extension
The file extension to append to the file name in the response (optional). The file extension to append to the file name in the response (optional).

View File

@ -0,0 +1,29 @@
# Staged Changes
!!! danger "Deprecated Feature"
This feature has been deprecated in NetBox v4.2 and will be removed in a future release. Please consider using the [netbox-branching plugin](https://github.com/netboxlabs/netbox-branching), which provides much more robust functionality.
A staged change represents the creation of a new object or the modification or deletion of an existing object to be performed at some future point. Each change must be assigned to a [branch](./branch.md).
Changes can be applied individually via the `apply()` method, however it is recommended to apply changes in bulk using the parent branch's `commit()` method.
## Fields
!!! warning
Staged changes are not typically created or manipulated directly, but rather effected through the use of the [`checkout()`](../../plugins/development/staged-changes.md) context manager.
### Branch
The [branch](./branch.md) to which this change belongs.
### Action
The type of action this change represents: `create`, `update`, or `delete`.
### Object
A generic foreign key referencing the existing object to which this change applies.
### Data
JSON representation of the changes being made to the object (not applicable for deletions).

View File

@ -1,43 +0,0 @@
# Table Configs
This object represents the saved configuration of an object table in NetBox. Table configs can be crafted, saved, and shared among users to apply specific views within object lists. Each table config can specify which table columns to display, the order in which to display them, and which columns are used for sorting.
For example, you might wish to create a table config for the devices list to assist in inventory tasks. This view might show the device name, location, serial number, and asset tag, but omit operational details like IP addresses. Once applied, this table config can be saved for reuse in future audits.
## Fields
### Name
A human-friendly name for the table config.
### User
The user to which this filter belongs. The current user will be assigned automatically when saving a table config via the UI, and cannot be changed.
### Object Type
The type of NetBox object to which the table config pertains.
### Table
The name of the specific table to which the table config pertains. (Some NetBox object use multiple tables.)
### Weight
A numeric weight used to influence the order in which table configs are listed. Table configs with a lower weight will be listed before those with a higher weight. Table configs having the same weight will be ordered alphabetically.
### Enabled
Determines whether this table config can be used. Disabled table configs will not appear as options in the UI, however they will be included in API results.
### Shared
Determines whether this table config is intended for use by all users or only its owner. Note that deselecting this option does **not** hide the table config from other users; it is merely excluded from the list of available table configs in UI object list views.
### Ordering
A list of column names by which the table is to be ordered. If left blank, the table's default ordering will be used.
### Columns
A list of columns to be displayed in the table. The table will render these columns in the order they appear in the list. At least one column must be selected.

View File

@ -16,12 +16,6 @@ A unique URL-friendly identifier. (This value will be used for filtering.) This
The color to use when displaying the tag in the NetBox UI. The color to use when displaying the tag in the NetBox UI.
### Weight
A numeric weight employed to influence the ordering of tags. Tags with a lower weight will be listed before those with higher weights. Values must be within the range **0** to **32767**.
!!! info "This field was introduced in NetBox v4.3."
### Object Types ### Object Types
The assignment of a tag may be limited to a prescribed set of objects. For example, it may be desirable to limit the application of a specific tag to only devices and virtual machines. The assignment of a tag may be limited to a prescribed set of objects. For example, it may be desirable to limit the application of a specific tag to only devices and virtual machines.

View File

@ -2,12 +2,6 @@
This model represents an arbitrary range of individual IPv4 or IPv6 addresses, inclusive of its starting and ending addresses. For instance, the range 192.0.2.10 to 192.0.2.20 has eleven members. (The total member count is available as the `size` property on an IPRange instance.) Like [prefixes](./prefix.md) and [IP addresses](./ipaddress.md), each IP range may optionally be assigned to a [VRF](./vrf.md). This model represents an arbitrary range of individual IPv4 or IPv6 addresses, inclusive of its starting and ending addresses. For instance, the range 192.0.2.10 to 192.0.2.20 has eleven members. (The total member count is available as the `size` property on an IPRange instance.) Like [prefixes](./prefix.md) and [IP addresses](./ipaddress.md), each IP range may optionally be assigned to a [VRF](./vrf.md).
Each IP range can be marked as populated, which instructs NetBox to treat the range as though every IP address within it has been created (even though these individual IP addresses don't actually exist in the database). This can be helpful in scenarios where the management of a subset of IP addresses has been deferred to an external system of record, such as a DHCP server. NetBox will prohibit the creation of individual IP addresses within a range that has been marked as populated.
An IP range can also be marked as utilized. This will cause its utilization to always be reported as 100% when viewing the range or when calculating the utilization of a parent prefix. (If not enabled, a range's utilization is calculated based on the number of IP addresses which have been created within it.)
Typically, IP ranges marked as populated should also be marked as utilized, although there may be scenarios where this is undesirable (e.g. when reclaiming old IP space). An IP range which has been marked as populated but _not_ marked as utilized will always report a utilization of 0%, as it cannot contain child IP addresses.
## Fields ## Fields
### VRF ### VRF
@ -35,12 +29,6 @@ The IP range's operational status. Note that the status of a range does _not_ ha
!!! tip !!! tip
Additional statuses may be defined by setting `IPRange.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. Additional statuses may be defined by setting `IPRange.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
### Mark Populated
!!! note "This field was added in NetBox v4.3."
If enabled, NetBox will treat this IP range as being fully populated when calculating available IP space. It will also prevent the creation of IP addresses which fall within the declared range (and assigned VRF, if any).
### Mark Utilized ### Mark Utilized
If enabled, the IP range will be considered 100% utilized regardless of how many IP addresses are defined within it. This is useful for documenting DHCP ranges, for example. If enabled, the IP range will be considered 100% utilized regardless of how many IP addresses are defined within it. This is useful for documenting DHCP ranges, for example.

View File

@ -6,15 +6,6 @@ To aid in the efficient creation of services, users may opt to first create a [s
## Fields ## Fields
### Parent
The parent object to which the service is assigned. This must be one of [Device](../dcim/device.md),
[VirtualMachine](../virtualization/virtualmachine.md), or [FHRP Group](./fhrpgroup.md).
!!! note "Changed in NetBox v4.3"
Previously, `parent` was a property that pointed to either a Device or Virtual Machine. With the capability to assign services to FHRP groups, this is a unified in a concrete field.
### Name ### Name
A service or protocol name. A service or protocol name.

View File

@ -4,11 +4,9 @@ A contact represents an individual or group that has been associated with an obj
## Fields ## Fields
### Groups ### Group
The [contact groups](./contactgroup.md) to which this contact is assigned (if any). The [contact group](./contactgroup.md) to which this contact is assigned (if any).
!!! info "This field was renamed from `group` to `groups` in NetBox v4.3, and now supports the assignment of a contact to more than one group."
### Name ### Name

View File

@ -33,19 +33,6 @@ The technology employed in forming and operating the L2VPN. Choices include:
!!! note !!! note
Designating the type as VPWS, EPL, EP-LAN, EP-TREE will limit the L2VPN instance to two terminations. Designating the type as VPWS, EPL, EP-LAN, EP-TREE will limit the L2VPN instance to two terminations.
### Status
The operational status of the L2VPN. By default, the following statuses are available:
* Active (default)
* Planned
* Faulty
!!! tip "Custom L2VPN statuses"
Additional L2VPN statuses may be defined by setting `L2VPN.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
!!! info "This field was introduced in NetBox v4.3."
### Identifier ### Identifier
An optional numeric identifier. This can be used to track a pseudowire ID, for example. An optional numeric identifier. This can be used to track a pseudowire ID, for example.

View File

@ -15,6 +15,7 @@ A background job implements a basic [Job](../../models/core/job.md) executor for
```python title="jobs.py" ```python title="jobs.py"
from netbox.jobs import JobRunner from netbox.jobs import JobRunner
class MyTestJob(JobRunner): class MyTestJob(JobRunner):
class Meta: class Meta:
name = "My Test Job" name = "My Test Job"
@ -24,8 +25,6 @@ class MyTestJob(JobRunner):
# your logic goes here # your logic goes here
``` ```
Completed jobs will have their status updated to "completed" by default, or "errored" if an unhandled exception was raised by the `run()` method. To intentionally mark a job as failed, raise the `core.exceptions.JobFailed` exception. (Note that "failed" differs from "errored" in that a failure may be expected under certain conditions, whereas an error is not.)
You can schedule the background job from within your code (e.g. from a model's `save()` method or a view) by calling `MyTestJob.enqueue()`. This method passes through all arguments to `Job.enqueue()`. However, no `name` argument must be passed, as the background job name will be used instead. You can schedule the background job from within your code (e.g. from a model's `save()` method or a view) by calling `MyTestJob.enqueue()`. This method passes through all arguments to `Job.enqueue()`. However, no `name` argument must be passed, as the background job name will be used instead.
!!! tip !!! tip

View File

@ -1,6 +1,6 @@
# Filters & Filter Sets # Filters & Filter Sets
Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI, REST API, or GraphQL API. NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets.
## FilterSet Classes ## FilterSet Classes
@ -61,11 +61,6 @@ class MyModelViewSet(...):
The `TagFilter` class is available for all models which support tag assignment (those which inherit from `NetBoxModel` or `TagsMixin`). This filter subclasses django-filter's `ModelMultipleChoiceFilter` to work with NetBox's `TaggedItem` class. The `TagFilter` class is available for all models which support tag assignment (those which inherit from `NetBoxModel` or `TagsMixin`). This filter subclasses django-filter's `ModelMultipleChoiceFilter` to work with NetBox's `TaggedItem` class.
This class filters `tags` using the `slug` field. For example:
`GET /api/dcim/sites/?tag=alpha&tag=bravo`
```python ```python
from django_filters import FilterSet from django_filters import FilterSet
from extras.filters import TagFilter from extras.filters import TagFilter
@ -73,19 +68,3 @@ from extras.filters import TagFilter
class MyModelFilterSet(FilterSet): class MyModelFilterSet(FilterSet):
tag = TagFilter() tag = TagFilter()
``` ```
### TagIDFilter
The `TagIDFilter` class is available for all models which support tag assignment (those which inherit from `NetBoxModel` or `TagsMixin`). This filter subclasses django-filter's `ModelMultipleChoiceFilter` to work with NetBox's `TaggedItem` class.
This class filters `tags` using the `id` field. For example:
`GET /api/dcim/sites/?tag_id=100&tag_id=200`
```python
from django_filters import FilterSet
from extras.filters import TagIDFilter
class MyModelFilterSet(FilterSet):
tag_id = TagIDFilter()
```

View File

@ -103,7 +103,6 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
| `name` | Raw plugin name; same as the plugin's source directory | | `name` | Raw plugin name; same as the plugin's source directory |
| `verbose_name` | Human-friendly name for the plugin | | `verbose_name` | Human-friendly name for the plugin |
| `version` | Current release ([semantic versioning](https://semver.org/) is encouraged) | | `version` | Current release ([semantic versioning](https://semver.org/) is encouraged) |
| `release_track` | An alternate release track (e.g. `dev` or `beta`) to which a release belongs |
| `description` | Brief description of the plugin's purpose | | `description` | Brief description of the plugin's purpose |
| `author` | Name of plugin's author | | `author` | Name of plugin's author |
| `author_email` | Author's public email address | | `author_email` | Author's public email address |

View File

@ -22,7 +22,7 @@ from netbox.plugins import PluginConfig
### ContentType renamed to ObjectType ### ContentType renamed to ObjectType
NetBox's proxy model for Django's [ContentType model](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#the-contenttype-model) has been renamed to ObjectType for clarity. In general, plugins should use the ObjectType proxy when referencing content types, as it includes several custom manager methods. The one exception to this is when defining [generic foreign keys](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#generic-relations): The ForeignKey field used for a GFK should point to Django's native ContentType. NetBox's proxy model for Django's [ContentType model](https://docs.djangoproject.com/en/5.0/ref/contrib/contenttypes/#the-contenttype-model) has been renamed to ObjectType for clarity. In general, plugins should use the ObjectType proxy when referencing content types, as it includes several custom manager methods. The one exception to this is when defining [generic foreign keys](https://docs.djangoproject.com/en/5.0/ref/contrib/contenttypes/#generic-relations): The ForeignKey field used for a GFK should point to Django's native ContentType.
Additionally, plugin maintainers are strongly encouraged to adopt the "object type" terminology for field and filter names wherever feasible to be consistent with NetBox core (however this is not required for compatibility). Additionally, plugin maintainers are strongly encouraged to adopt the "object type" terminology for field and filter names wherever feasible to be consistent with NetBox core (however this is not required for compatibility).

View File

@ -117,10 +117,6 @@ For more information about database migrations, see the [Django documentation](h
::: netbox.models.features.CloningMixin ::: netbox.models.features.CloningMixin
::: netbox.models.features.ContactsMixin
!!! info "Plugin support for ContactsMixin was introduced in NetBox v4.3."
::: netbox.models.features.CustomLinksMixin ::: netbox.models.features.CustomLinksMixin
::: netbox.models.features.CustomFieldsMixin ::: netbox.models.features.CustomFieldsMixin
@ -129,6 +125,9 @@ For more information about database migrations, see the [Django documentation](h
::: netbox.models.features.EventRulesMixin ::: netbox.models.features.EventRulesMixin
!!! note
`EventRulesMixin` was renamed from `WebhooksMixin` in NetBox v3.7.
::: netbox.models.features.ExportTemplatesMixin ::: netbox.models.features.ExportTemplatesMixin
::: netbox.models.features.JobsMixin ::: netbox.models.features.JobsMixin

View File

@ -65,11 +65,10 @@ item1 = PluginMenuItem(
A `PluginMenuItem` has the following attributes: A `PluginMenuItem` has the following attributes:
| Attribute | Required | Description | | Attribute | Required | Description |
|-----------------|----------|----------------------------------------------------------------------------------------------------------| |---------------|----------|----------------------------------------------------------------------------------------------------------|
| `link` | Yes | Name of the URL path to which this menu item links | | `link` | Yes | Name of the URL path to which this menu item links |
| `link_text` | Yes | The text presented to the user | | `link_text` | Yes | The text presented to the user |
| `permissions` | - | A list of permissions required to display this link | | `permissions` | - | A list of permissions required to display this link |
| `auth_required` | - | Display only for authenticated users |
| `staff_only` | - | Display only for users who have `is_staff` set to true (any specified permissions will also be required) | | `staff_only` | - | Display only for users who have `is_staff` set to true (any specified permissions will also be required) |
| `buttons` | - | An iterable of PluginMenuButton instances to include | | `buttons` | - | An iterable of PluginMenuButton instances to include |

View File

@ -0,0 +1,39 @@
# Staged Changes
!!! danger "Deprecated Feature"
This feature has been deprecated in NetBox v4.2 and will be removed in a future release. Please consider using the [netbox-branching plugin](https://github.com/netboxlabs/netbox-branching), which provides much more robust functionality.
NetBox provides a programmatic API to stage the creation, modification, and deletion of objects without actually committing those changes to the active database. This can be useful for performing a "dry run" of bulk operations, or preparing a set of changes for administrative approval, for example.
To begin staging changes, first create a [branch](../../models/extras/branch.md):
```python
from extras.models import Branch
branch1 = Branch.objects.create(name='branch1')
```
Then, activate the branch using the `checkout()` context manager and begin making your changes. This initiates a new database transaction.
```python
from extras.models import Branch
from netbox.staging import checkout
branch1 = Branch.objects.get(name='branch1')
with checkout(branch1):
Site.objects.create(name='New Site', slug='new-site')
# ...
```
Upon exiting the context, the database transaction is automatically rolled back and your changes recorded as [staged changes](../../models/extras/stagedchange.md). Re-entering a branch will trigger a new database transaction and automatically apply any staged changes associated with the branch.
To apply the changes within a branch, call the branch's `commit()` method:
```python
from extras.models import Branch
branch1 = Branch.objects.get(name='branch1')
branch1.commit()
```
Committing a branch is an all-or-none operation: Any exceptions will revert the entire set of changes. After successfully committing a branch, all its associated StagedChange objects are automatically deleted (however the branch itself will remain and can be reused).

View File

@ -1,6 +1,6 @@
# Views # Views
## Writing Basic Views ## Writing Views
If your plugin will provide its own page or pages within the NetBox web UI, you'll need to define views. A view is a piece of business logic which performs an action and/or renders a page when a request is made to a particular URL. HTML content is rendered using a [template](./templates.md). Views are typically defined in `views.py`, and URL patterns in `urls.py`. If your plugin will provide its own page or pages within the NetBox web UI, you'll need to define views. A view is a piece of business logic which performs an action and/or renders a page when a request is made to a particular URL. HTML content is rendered using a [template](./templates.md). Views are typically defined in `views.py`, and URL patterns in `urls.py`.
@ -47,13 +47,9 @@ A URL pattern has three components:
This makes our view accessible at the URL `/plugins/animal-sounds/random/`. (Remember, our `AnimalSoundsConfig` class sets our plugin's base URL to `animal-sounds`.) Viewing this URL should show the base NetBox template with our custom content inside it. This makes our view accessible at the URL `/plugins/animal-sounds/random/`. (Remember, our `AnimalSoundsConfig` class sets our plugin's base URL to `animal-sounds`.) Viewing this URL should show the base NetBox template with our custom content inside it.
## NetBox Model Views
NetBox provides several generic view classes and additional helper functions, to simplify the implementation of plugin logic. These are recommended to be used whenever possible to keep the maintenance overhead of plugins low.
### View Classes ### View Classes
Generic view classes (documented below) facilitate common operations, such as creating, viewing, modifying, and deleting objects. Plugins can subclass these views for their own use. NetBox provides several generic view classes (documented below) to facilitate common operations, such as creating, viewing, modifying, and deleting objects. Plugins can subclass these views for their own use.
| View Class | Description | | View Class | Description |
|----------------------|--------------------------------------------------------| |----------------------|--------------------------------------------------------|
@ -69,51 +65,18 @@ Generic view classes (documented below) facilitate common operations, such as cr
!!! warning !!! warning
Please note that only the classes which appear in this documentation are currently supported. Although other classes may be present within the `views.generic` module, they are not yet supported for use by plugins. Please note that only the classes which appear in this documentation are currently supported. Although other classes may be present within the `views.generic` module, they are not yet supported for use by plugins.
### URL registration #### Example Usage
The NetBox URL registration process has two parts:
1. View classes can be decorated with `@register_model_view()`. This registers a new URL for the model.
2. All of a model's URLs can be included in `urls.py` using the `get_model_urls()` function. This call is usually required twice: once to import general views for the model and again to import model detail views tied to the object's primary key.
::: utilities.views.register_model_view
!!! note "Changed in NetBox v4.2"
In NetBox v4.2, the `register_model_view()` function was extended to support the registration of list views by passing `detail=False`.
::: utilities.urls.get_model_urls
!!! note "Changed in NetBox v4.2"
In NetBox v4.2, the `get_model_urls()` function was extended to support retrieving registered general model views (e.g. for listing objects) by passing `detail=False`.
### Example Usage
```python ```python
# views.py # views.py
from netbox.views.generic import ObjectEditView from netbox.views.generic import ObjectEditView
from utilities.views import register_model_view
from .models import Thing from .models import Thing
@register_model_view(Thing, name='add', detail=False)
@register_model_view(Thing, name='edit')
class ThingEditView(ObjectEditView): class ThingEditView(ObjectEditView):
queryset = Thing.objects.all() queryset = Thing.objects.all()
template_name = 'myplugin/thing.html' template_name = 'myplugin/thing.html'
... ...
``` ```
```python
# urls.py
from django.urls import include, path
from utilities.urls import get_model_urls
urlpatterns = [
path('thing/', include(get_model_urls('myplugin', 'thing', detail=False))),
path('thing/<int:pk>/', include(get_model_urls('myplugin', 'thing'))),
...
]
```
## Object Views ## Object Views
Below are the class definitions for NetBox's object views. These views handle CRUD actions for individual objects. The view, add/edit, and delete views each inherit from `BaseObjectView`, which is not intended to be used directly. Below are the class definitions for NetBox's object views. These views handle CRUD actions for individual objects. The view, add/edit, and delete views each inherit from `BaseObjectView`, which is not intended to be used directly.
@ -180,9 +143,6 @@ Below are the class definitions for NetBox's multi-object views. These views han
These views are provided to enable or enhance certain NetBox model features, such as change logging or journaling. These typically do not need to be subclassed: They can be used directly e.g. in a URL path. These views are provided to enable or enhance certain NetBox model features, such as change logging or journaling. These typically do not need to be subclassed: They can be used directly e.g. in a URL path.
!!! note
These feature views are automatically registered for all models that implement the respective feature. There is usually no need to override them. However, if that's the case, the URL must be registered manually in `urls.py` instead of using the `register_model_view()` function or decorator.
::: netbox.views.generic.ObjectChangeLogView ::: netbox.views.generic.ObjectChangeLogView
options: options:
members: members:
@ -197,7 +157,7 @@ These views are provided to enable or enhance certain NetBox model features, suc
### Additional Tabs ### Additional Tabs
Plugins can "attach" a custom view to a NetBox model by registering it with `register_model_view()`. To include a tab for this view within the NetBox UI, declare a TabView instance named `tab`, and add it to the template context dict: Plugins can "attach" a custom view to a core NetBox model by registering it with `register_model_view()`. To include a tab for this view within the NetBox UI, declare a TabView instance named `tab`, and add it to the template context dict:
```python ```python
from dcim.models import Site from dcim.models import Site
@ -225,6 +185,11 @@ class MyView(generic.ObjectView):
) )
``` ```
!!! note "Changed in NetBox v4.2"
The `register_model_view()` function was extended in NetBox v4.2 to support registration of list views by passing `detail=False`.
::: utilities.views.register_model_view
::: utilities.views.ViewTab ::: utilities.views.ViewTab
### Extra Template Content ### Extra Template Content
@ -233,7 +198,6 @@ Plugins can inject custom content into certain areas of core NetBox views. This
| Method | View | Description | | Method | View | Description |
|---------------------|-------------|-----------------------------------------------------| |---------------------|-------------|-----------------------------------------------------|
| `head()` | All | Custom HTML `<head>` block includes |
| `navbar()` | All | Inject content inside the top navigation bar | | `navbar()` | All | Inject content inside the top navigation bar |
| `list_buttons()` | List view | Add buttons to the top of the page | | `list_buttons()` | List view | Add buttons to the top of the page |
| `buttons()` | Object view | Add buttons to the top of the page | | `buttons()` | Object view | Add buttons to the top of the page |

View File

@ -86,69 +86,3 @@ netbox=> DELETE FROM django_migrations WHERE app='pluginname';
!!! warning !!! warning
Exercise extreme caution when altering Django system tables. Users are strongly encouraged to perform a backup of their database immediately before taking these actions. Exercise extreme caution when altering Django system tables. Users are strongly encouraged to perform a backup of their database immediately before taking these actions.
## Clean Up Content Types and Permissions
After removing a plugin and its database tables, you may find that object type references (`ContentTypes`) created by the plugin still appear in the permissions management section (e.g., when editing permissions in the NetBox UI).
This happens because the `django_content_type` table retains entries for the models that the plugin registered with Django.
!!! warning
Please use caution when removing `ContentTypes`. It is strongly recommended to **back up your database** before making these changes.
**Identify Stale Content Types:**
Open the Django shell to inspect lingering `ContentType` entries related to the removed plugin.
Typically, the Content Type's `app_label` matches the plugins name.
```no-highlight
$ cd /opt/netbox/
$ source /opt/netbox/venv/bin/activate
(venv) $ python3 netbox/manage.py nbshell
```
Then, in the shell:
```no-highlight
from django.contrib.contenttypes.models import ContentType
# Replace 'pluginname' with your plugin's actual name
stale_types = ContentType.objects.filter(app_label="pluginname")
for ct in stale_types:
print(ct)
### ^^^ These will be removed, make sure its ok
```
!!! warning
Review the output carefully and confirm that each listed Content Type is related to the plugin you removed.
**Remove Stale Content Types and Related Permissions:**
Next, check for any permissions associated with these Content Types:
```no-highlight
from django.contrib.auth.models import Permission
for ct in stale_types:
perms = Permission.objects.filter(content_type=ct)
print(list(perms))
```
If there are related Permissions, you can remove them safely:
```no-highlight
for ct in stale_types:
Permission.objects.filter(content_type=ct).delete()
```
After removing any related permissions, delete the Content Type entries:
```no-highlight
stale_types.delete()
```
**Restart NetBox:**
After making these changes, restart the NetBox service to ensure all changes are reflected.
```no-highlight
sudo systemctl restart netbox
```

View File

@ -10,15 +10,6 @@ Minor releases are published in April, August, and December of each calendar yea
This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release. This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release.
#### [Version 4.3](./version-4.3.md) (May 2025)
* Module Type Profiles & Custom Attributes ([#19002](https://github.com/netbox-community/netbox/issues/19002))
* Reusable Table Configurations ([#14591](https://github.com/netbox-community/netbox/issues/14591))
* Option to Treat IP Ranges as Fully Populated ([#9763](https://github.com/netbox-community/netbox/issues/9763))
* Hierarchical Device Roles ([#18245](https://github.com/netbox-community/netbox/issues/18245))
* Periodic Synchronization of Data Sources ([#18287](https://github.com/netbox-community/netbox/issues/18287))
* Proxy Routing ([#18627](https://github.com/netbox-community/netbox/issues/18627))
#### [Version 4.2](./version-4.2.md) (January 2025) #### [Version 4.2](./version-4.2.md) (January 2025)
* Assign Multiple MAC Addresses per Interface ([#4867](https://github.com/netbox-community/netbox/issues/4867)) * Assign Multiple MAC Addresses per Interface ([#4867](https://github.com/netbox-community/netbox/issues/4867))

View File

@ -1,242 +0,0 @@
# NetBox v4.3
## v4.3.4 (2025-07-15)
### Enhancements
* [#18811](https://github.com/netbox-community/netbox/issues/18811) - Match expanded form IPv6 addresses in global search
* [#19550](https://github.com/netbox-community/netbox/issues/19550) - Enable lazy loading for rack elevations
* [#19571](https://github.com/netbox-community/netbox/issues/19571) - Add a default module type profile for expansion cards
* [#19793](https://github.com/netbox-community/netbox/issues/19793) - Support custom dynamic navigation menu links
* [#19828](https://github.com/netbox-community/netbox/issues/19828) - Expose L2VPN termination in interface GraphQL response
### Bug Fixes
* [#19413](https://github.com/netbox-community/netbox/issues/19413) - Custom fields should be grouped in filter forms
* [#19633](https://github.com/netbox-community/netbox/issues/19633) - Introduce InvalidCondition exception and log all evaluations of invalid event rule conditions
* [#19800](https://github.com/netbox-community/netbox/issues/19800) - Module type bulk import should support profile assignment
* [#19806](https://github.com/netbox-community/netbox/issues/19806) - Introduce JobFailed exception to allow marking background jobs as failed
* [#19827](https://github.com/netbox-community/netbox/issues/19827) - Enforce uniqueness for device role names & slugs
* [#19839](https://github.com/netbox-community/netbox/issues/19839) - Enable export of parent assignment for recursively nested objects
* [#19876](https://github.com/netbox-community/netbox/issues/19876) - Remove Markdown rendering from CustomFieldChoiceSet description field
---
## v4.3.3 (2025-06-26)
### Enhancements
* [#17183](https://github.com/netbox-community/netbox/issues/17183) - Enable associating tags with object types during bulk import
* [#17719](https://github.com/netbox-community/netbox/issues/17719) - Introduce a user preference for table row striping
* [#19492](https://github.com/netbox-community/netbox/issues/19492) - Add a UI button to download the output of an executed custom script
* [#19499](https://github.com/netbox-community/netbox/issues/19499) - Support qualifying interfaces by parent device when bulk importing wireless links
### Bug Fixes
* [#19529](https://github.com/netbox-community/netbox/issues/19529) - Fix support for running custom scripts via the `runscript` management command
* [#19555](https://github.com/netbox-community/netbox/issues/19555) - Fix support for `schedule_at` when invoking a custom script via the REST API
* [#19617](https://github.com/netbox-community/netbox/issues/19617) - Ensure consistent styling of "connect" buttons in UI
* [#19640](https://github.com/netbox-community/netbox/issues/19640) - Restore ability to filter FHRP group assignments by device/VM in GraphQL API
* [#19644](https://github.com/netbox-community/netbox/issues/19644) - Atomic transactions should always employ database routing
* [#19659](https://github.com/netbox-community/netbox/issues/19659) - Populate initial device/VM selection for "add a service" button
* [#19665](https://github.com/netbox-community/netbox/issues/19665) - Correct field reference in wireless link model validation
* [#19667](https://github.com/netbox-community/netbox/issues/19667) - Fix `TypeError` exception when creating a new module profile type with no schema
* [#19673](https://github.com/netbox-community/netbox/issues/19673) - Ignore custom field references when compiling table prefetches
* [#19677](https://github.com/netbox-community/netbox/issues/19677) - Fix exception when passing null value to `present_in_vrf` filter
* [#19680](https://github.com/netbox-community/netbox/issues/19680) - Correct chronological ordering of change records resulting from device deletions
* [#19687](https://github.com/netbox-community/netbox/issues/19687) - Cellular interface types should be considered non-connectable
* [#19702](https://github.com/netbox-community/netbox/issues/19702) - Fix `DoesNotExist` exception when deleting a notification group with an associated event rule
* [#19745](https://github.com/netbox-community/netbox/issues/19745) - Fix bulk import of services with IP addresses assigned to FHRP groups
---
## v4.3.2 (2025-06-05)
### Enhancements
* [#19200](https://github.com/netbox-community/netbox/issues/19200) - Display assigned virtual chassis (if any) on device view
* [#19461](https://github.com/netbox-community/netbox/issues/19461) - Add color backgrounds for virtual circuit types
* [#19605](https://github.com/netbox-community/netbox/issues/19605) - Enable filtering IP addresses by family in GraphQL API
* [#19627](https://github.com/netbox-community/netbox/issues/19627) - Introduce object change migrators
### Bug Fixes
* [#19415](https://github.com/netbox-community/netbox/issues/19415) - Increase maximum supported distance for circuits and wireless links
* [#19475](https://github.com/netbox-community/netbox/issues/19475) - VLANs belonging to the same location as a VM's cluster should be eligible for assignment to interfaces on that VM
* [#19486](https://github.com/netbox-community/netbox/issues/19486) - Fix connection card rendering for console server ports
* [#19487](https://github.com/netbox-community/netbox/issues/19487) - Fix `FieldError` exception when ordering circuit or tunnel terminations by the terminating object
* [#19490](https://github.com/netbox-community/netbox/issues/19490) - Fix inclusion support for config templates populated via a data source
* [#19496](https://github.com/netbox-community/netbox/issues/19496) - Fix `AttributeError` exception when rendering a config template with no output
* [#19510](https://github.com/netbox-community/netbox/issues/19510) - Restore GraphQL API filtering for assigned IP addresses
* [#19520](https://github.com/netbox-community/netbox/issues/19520) - Restore ability to alter prefix scope via the REST API
* [#19587](https://github.com/netbox-community/netbox/issues/19587) - The `occupied` filter should include interfaces terminating a wireless link
* [#19599](https://github.com/netbox-community/netbox/issues/19599) - Fix `AttributeError` exception when sorting change history under user view
* [#19610](https://github.com/netbox-community/netbox/issues/19610) - Fix `FieldError` exception when sorting tunnel terminations by tenant
* [#19623](https://github.com/netbox-community/netbox/issues/19623) - Display description under provider account view
---
## v4.3.1 (2025-05-13)
### Enhancements
* [#17073](https://github.com/netbox-community/netbox/issues/17073) - Enable global search for tags
* [#18419](https://github.com/netbox-community/netbox/issues/18419) - Enable specifying a queue name when calling `Job.enqueue()`
* [#19416](https://github.com/netbox-community/netbox/issues/19416) - Add the 1000BASE-SX interface type
* [#19434](https://github.com/netbox-community/netbox/issues/19434) - Add pre-populated interface speed choices for 2.5 and 5 Gbps
### Bug Fixes
* [#17107](https://github.com/netbox-community/netbox/issues/17107) - Fix cosmetic issue in cable traces ending at a provider network
* [#19309](https://github.com/netbox-community/netbox/issues/19309) - Improve REST API query performance for prefixes and IP addresses
* [#19361](https://github.com/netbox-community/netbox/issues/19361) - Fix incorrect GraphQL object types
* [#19375](https://github.com/netbox-community/netbox/issues/19375) - Fix table configuration after applying a saved table config
* [#19376](https://github.com/netbox-community/netbox/issues/19376) - Fix `FieldDoesNotExist` exception when global search results include a contact
* [#19380](https://github.com/netbox-community/netbox/issues/19380) - Fix column selections for child object tables
* [#19381](https://github.com/netbox-community/netbox/issues/19381) - Fix syncing of custom scripts from a remote data source
* [#19396](https://github.com/netbox-community/netbox/issues/19396) - Enable nullifying VLAN `qinq_role` via the REST API
* [#19397](https://github.com/netbox-community/netbox/issues/19397) - Correct enum type for IPRangeFilter in GraphQL API
* [#19432](https://github.com/netbox-community/netbox/issues/19432) - Update minimum required PostgreSQL version referenced by server error page
* [#19440](https://github.com/netbox-community/netbox/issues/19440) - Ensure data migrations use the correct database connection
* [#19444](https://github.com/netbox-community/netbox/issues/19444) - Fix change logging for contact group assignments
* [#19463](https://github.com/netbox-community/netbox/issues/19463) - Hide button dropdown for tables which do not support saved configs
* [#19464](https://github.com/netbox-community/netbox/issues/19464) - Fix bulk editing of inventory items from device view
* [#19465](https://github.com/netbox-community/netbox/issues/19465) - Fix ability to clear assigned prefix scope in UI
* [#19472](https://github.com/netbox-community/netbox/issues/19472) - Fix device column rendering in virtual device contexts table
---
## v4.3.0 (2025-05-01)
### Breaking Changes
* The GraphQL API Now uses an advanced syntax for filtering, to enable e.g. logical AND/OR filtering and custom field lookups.
* PostgreSQL 13 is no longer supported. NetBox v4.3 requires PostgreSQL 14.0 or later.
* The `ALLOW_TOKEN_RETRIEVAL` configuration parameter now defaults to False.
* The `device` and `virtual_machine` foreign keys on the Service model have been replaced with a generic `parent` relationship to support the assignment of services to FHRP groups as well.
* The `group` foreign key on the Contact model has been replaced with a many-to-many `groups` field.
* `django-storages` is now a required dependency. (It will be installed automatically on upgrade.)
* PluginTemplateExtension no longer supports registration via the singular `model` attribute (use `models` instead).
* The legacy staged changes functionality has been removed.
### New Features
#### Module Type Profiles & Custom Attributes ([#19002](https://github.com/netbox-community/netbox/issues/19002))
The new [module type profile](../models/dcim/moduletypeprofile.md) model enables users to declare custom profiles for module types, with the ability to define custom attributes for each profile according to its functional role. For example, a CPU module type might declare architecture and clock speed attributes; a hard disk profile might declare attributes for type and speed.
Attributes can be declared on each profile using [JSON schema](https://json-schema.org/), which allows for attributes to be declared as strings (text), integers, decimals, booleans, or choice fields. Profile attributes render as individual form fields when modifying a module type. Several profiles have been included by default to serve as examples, however these may be modified or removed.
#### Reusable Table Configurations ([#14591](https://github.com/netbox-community/netbox/issues/14591))
After modifying the displayed columns and/or ordering for a specific object table in the user interface, users now have the option to save that configuration so that it can be reused in the future. Similar to saved filters, table configs can be shared with other users to easily replicate table layouts crafted to serve specific use cases.
#### Option to Treat IP Ranges as Fully Populated ([#9763](https://github.com/netbox-community/netbox/issues/9763))
A new `mark_populated` boolean field has been added to the IPRange model. If set to true, NetBox will consider the IP range to be fully populated, and will not permit the creation of individual IP addresses within the range. For example, you might defer the management of an IP range to an external DHCP server, and wish for NetBox to treat the range as a opaque monolithic block for planning and allocation purposes.
#### Hierarchical Device Roles ([#18245](https://github.com/netbox-community/netbox/issues/18245))
Device roles can now be arranged hierarchically, with one role optionally serving as a parent to one or more child roles. For example, you might wish to create a generic "Server" role for devices with "Application Server" and "Database Server" roles beneath it. A device could then be assigned to any of these three roles.
#### Periodic Synchronization of Data Sources ([#18287](https://github.com/netbox-community/netbox/issues/18287))
Data sources can now be configured to synchronize automatically at a specified interval, as indicated by the new `sync_interval` field. No additional system configuration is necessary to support this functionality; background jobs will be scheduled automatically by the RQ worker process.
#### Proxy Routing ([#18627](https://github.com/netbox-community/netbox/issues/18627))
User can now declare one or more proxy routers via the `PROXY_ROUTERS` configuration parameter to control the use of specific proxy servers for various outbound connections. For example, it is now possible to configure NetBox to use different proxies based on the type of outbound traffic or its destination.
### Enhancements
* [#7598](https://github.com/netbox-community/netbox/issues/7598) - Adopt advanced query filtering in GraphQL API to support filtering by custom fields
* [#8423](https://github.com/netbox-community/netbox/issues/8423) - Enable assigning services to FHRP groups
* [#15842](https://github.com/netbox-community/netbox/issues/15842) - Introduce the `LOGIN_FORM_HIDDEN` configuration parameter
* [#16224](https://github.com/netbox-community/netbox/issues/16224) - Implement pagination support for the GraphQL API
* [#17170](https://github.com/netbox-community/netbox/issues/17170) - Enable the assignment of a contact to multiple contact groups
* [#17443](https://github.com/netbox-community/netbox/issues/17443) - Add a `file_name` field to the export template model
* [#17602](https://github.com/netbox-community/netbox/issues/17602) - Add a `comments` field to all nested group models (Region, SiteGroup, Location, ContactGroup, TenantGroup, and WirelessLANGroup)
* [#17608](https://github.com/netbox-community/netbox/issues/17608) - Add a `status` field to the L2VPN model
* [#17653](https://github.com/netbox-community/netbox/issues/17653) - Enable declaring Jinja environment parameters on export templates (similar to config templates)
* [#17793](https://github.com/netbox-community/netbox/issues/17793) - Introduce a REST API endpoint for tagged objects (`/api/extras/tagged-objects/`)
* [#17841](https://github.com/netbox-community/netbox/issues/17841) - Add a `weight` field to the Tag model to influence ordering
* [#18296](https://github.com/netbox-community/netbox/issues/18296) - Add a `tenant` field to the VLAN group model
* [#18352](https://github.com/netbox-community/netbox/issues/18352) - Add a `status` field to the power outlet model
* [#18417](https://github.com/netbox-community/netbox/issues/18417) - Add an `outer_height` field to the rack & rack type models
* [#18535](https://github.com/netbox-community/netbox/issues/18535) - The presence of incompatible plugins will no longer prevent NetBox from starting
* [#18780](https://github.com/netbox-community/netbox/issues/18780) - Introduce `DATABASES` and `DATABASE_ROUTERS` configuration parameters to enable defining connections to external databases (e.g. for plugins)
* [#18783](https://github.com/netbox-community/netbox/issues/18783) - Enable filtering all applicable models by tag ID
* [#18785](https://github.com/netbox-community/netbox/issues/18785) - Enable custom choices for rack, device, and module airflow
* [#18896](https://github.com/netbox-community/netbox/issues/18896) - Enable the use of remote storage for custom scripts
### Plugins
* [#16630](https://github.com/netbox-community/netbox/issues/16630) - Plugins can now inject content within the HTML `<head>` block via the new `plugin_head()` method on PluginTemplateExtension
* [#17424](https://github.com/netbox-community/netbox/issues/17424) - Extend ViewTab with a `visible` argument to control tab rendering
* [#17857](https://github.com/netbox-community/netbox/issues/17857) - Added a `release_track` attribute to PluginConfig
* [#18305](https://github.com/netbox-community/netbox/issues/18305) - Introduce plugin support for ContactsMixin
* [#19073](https://github.com/netbox-community/netbox/issues/19073) - Allow installed plugins to be omitted from the plugins list
### Other Changes
* [#18071](https://github.com/netbox-community/netbox/issues/18071) - Removed legacy staged changed functionality in favor of the [netbox-branching](https://github.com/netboxlabs/netbox-branching) plugin
* [#18072](https://github.com/netbox-community/netbox/issues/18072) - Drop support for the singular `model` attribute on PluginTemplateExtension (use `models` instead)
* [#18191](https://github.com/netbox-community/netbox/issues/18191) - Remove redundant PostgreSQL indexes
* [#18236](https://github.com/netbox-community/netbox/issues/18236) - Upgrade the HTMX library to v2.0
* [#18540](https://github.com/netbox-community/netbox/issues/18540) - Operational plugins are now recorded in the application registry
* [#18623](https://github.com/netbox-community/netbox/issues/18623) - Upgrade the Tabler CSS theme to v1.2
* [#18743](https://github.com/netbox-community/netbox/issues/18743) - Upgrade Django to v5.2
* [#18751](https://github.com/netbox-community/netbox/issues/18751) - Change the default value for `ALLOW_TOKEN_RETRIEVAL` to False
* [#18808](https://github.com/netbox-community/netbox/issues/18808) - Squashed migration dependencies have been altered to rectify an issue with Django's `sqlmigrate` management command
* [#18820](https://github.com/netbox-community/netbox/issues/18820) - PostgreSQL 13 is no longer supported
* [#19004](https://github.com/netbox-community/netbox/issues/19004) - The use of inventory items has been deprecated in favor of modules. Inventory items and roles may be removed in a future NetBox release.
### REST API Changes
* Added the following endpoints:
* `/api/extras/table-configs/`
* `/api/extras/tagged-objects/`
* `/api/dcim/module-type-profiles/`
* core.DataSource
* Added the optional `sync_interval` field
* dcim.DeviceRole
* Added the optional `parent` recursive foreign key field to effect hierarchical ordering
* Added a `comments` field
* dcim.Location
* Added a `comments` field
* dcim.ModuleType
* Added the optional `profile` foreign key to the new ModuleTypeProfile model
* dcim.PowerOutlet
* Added a `status` field
* dcim.Rack
* Added the optional `outer_height` field
* dcim.RackType
* Added the optional `outer_height` field
* dcim.Region
* Added a `comments` field
* dcim.SiteGroup
* Added a `comments` field
* extras.ConfigTemplate
* Added optional fields `mime_type`, `file_name`, `file_extension` and `as_attachment`
* extras.ExportTemplate
* Added optional fields `file_name` and `environment_params` (JSON)
* extras.Tag
* Added a `weight` field
* ipam.IPRange
* Added a `mark_populaed` boolean field
* ipam.L2VPN
* Added a `status` field
* ipam.Service
* Removed the `device` and `virtual_machine` foreign key fields
* Added the `parent_object_type`, `parent_object_id`, and (read-only) `parent` fields
* ipam.VLANGroup
* Added the optional `tenant` foreign key field
* tenancy.Contact
* Removed the `group` foreign key field
* Added the `groups` many-to-many field
* tenancy.ContactGroup
* Added a `comments` field
* tenancy.TenantGroup
* Added a `comments` field
* wireless.WirelessLANGroup
* Added a `comments` field

View File

@ -49,7 +49,6 @@ markdown_extensions:
- admonition - admonition
- attr_list - attr_list
- footnotes - footnotes
- md_in_html
- pymdownx.emoji: - pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg emoji_generator: !!python/name:material.extensions.emoji.to_svg
@ -148,6 +147,7 @@ nav:
- GraphQL API: 'plugins/development/graphql-api.md' - GraphQL API: 'plugins/development/graphql-api.md'
- Background Jobs: 'plugins/development/background-jobs.md' - Background Jobs: 'plugins/development/background-jobs.md'
- Dashboard Widgets: 'plugins/development/dashboard-widgets.md' - Dashboard Widgets: 'plugins/development/dashboard-widgets.md'
- Staged Changes: 'plugins/development/staged-changes.md'
- Exceptions: 'plugins/development/exceptions.md' - Exceptions: 'plugins/development/exceptions.md'
- Migrating to v4.0: 'plugins/development/migration-v4.md' - Migrating to v4.0: 'plugins/development/migration-v4.md'
- Administration: - Administration:
@ -203,7 +203,6 @@ nav:
- ModuleBay: 'models/dcim/modulebay.md' - ModuleBay: 'models/dcim/modulebay.md'
- ModuleBayTemplate: 'models/dcim/modulebaytemplate.md' - ModuleBayTemplate: 'models/dcim/modulebaytemplate.md'
- ModuleType: 'models/dcim/moduletype.md' - ModuleType: 'models/dcim/moduletype.md'
- ModuleTypeProfile: 'models/dcim/moduletypeprofile.md'
- Platform: 'models/dcim/platform.md' - Platform: 'models/dcim/platform.md'
- PowerFeed: 'models/dcim/powerfeed.md' - PowerFeed: 'models/dcim/powerfeed.md'
- PowerOutlet: 'models/dcim/poweroutlet.md' - PowerOutlet: 'models/dcim/poweroutlet.md'
@ -224,6 +223,7 @@ nav:
- VirtualDeviceContext: 'models/dcim/virtualdevicecontext.md' - VirtualDeviceContext: 'models/dcim/virtualdevicecontext.md'
- Extras: - Extras:
- Bookmark: 'models/extras/bookmark.md' - Bookmark: 'models/extras/bookmark.md'
- Branch: 'models/extras/branch.md'
- ConfigContext: 'models/extras/configcontext.md' - ConfigContext: 'models/extras/configcontext.md'
- ConfigTemplate: 'models/extras/configtemplate.md' - ConfigTemplate: 'models/extras/configtemplate.md'
- CustomField: 'models/extras/customfield.md' - CustomField: 'models/extras/customfield.md'
@ -236,8 +236,8 @@ nav:
- Notification: 'models/extras/notification.md' - Notification: 'models/extras/notification.md'
- NotificationGroup: 'models/extras/notificationgroup.md' - NotificationGroup: 'models/extras/notificationgroup.md'
- SavedFilter: 'models/extras/savedfilter.md' - SavedFilter: 'models/extras/savedfilter.md'
- StagedChange: 'models/extras/stagedchange.md'
- Subscription: 'models/extras/subscription.md' - Subscription: 'models/extras/subscription.md'
- TableConfig: 'models/extras/tableconfig.md'
- Tag: 'models/extras/tag.md' - Tag: 'models/extras/tag.md'
- Webhook: 'models/extras/webhook.md' - Webhook: 'models/extras/webhook.md'
- IPAM: - IPAM:
@ -309,7 +309,6 @@ nav:
- git Cheat Sheet: 'development/git-cheat-sheet.md' - git Cheat Sheet: 'development/git-cheat-sheet.md'
- Release Notes: - Release Notes:
- Summary: 'release-notes/index.md' - Summary: 'release-notes/index.md'
- Version 4.3: 'release-notes/version-4.3.md'
- Version 4.2: 'release-notes/version-4.2.md' - Version 4.2: 'release-notes/version-4.2.md'
- Version 4.1: 'release-notes/version-4.1.md' - Version 4.1: 'release-notes/version-4.1.md'
- Version 4.0: 'release-notes/version-4.0.md' - Version 4.0: 'release-notes/version-4.0.md'

View File

@ -8,7 +8,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('users', '0002_squashed_0004'), ('users', '0004_netboxgroup_netboxuser'),
] ]
operations = [ operations = [

View File

@ -91,12 +91,10 @@ class LoginView(View):
if request.user.is_authenticated: if request.user.is_authenticated:
logger = logging.getLogger('netbox.auth.login') logger = logging.getLogger('netbox.auth.login')
return self.redirect_to_next(request, logger) return self.redirect_to_next(request, logger)
login_form_hidden = settings.LOGIN_FORM_HIDDEN
return render(request, self.template_name, { return render(request, self.template_name, {
'form': form, 'form': form,
'auth_backends': self.get_auth_backends(request), 'auth_backends': self.get_auth_backends(request),
'login_form_hidden': login_form_hidden,
}) })
def post(self, request): def post(self, request):
@ -191,10 +189,12 @@ class ProfileView(LoginRequiredMixin, View):
def get(self, request): def get(self, request):
# Compile changelog table # Compile changelog table
changelog = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(user=request.user)[:20] changelog = ObjectChange.objects.valid_models().restrict(request.user, 'view').filter(
user=request.user
).prefetch_related(
'changed_object_type'
)[:20]
changelog_table = ObjectChangeTable(changelog) changelog_table = ObjectChangeTable(changelog)
changelog_table.orderable = False
changelog_table.configure(request)
return render(request, self.template_name, { return render(request, self.template_name, {
'changelog_table': changelog_table, 'changelog_table': changelog_table,

View File

@ -16,7 +16,6 @@ from utilities.forms import get_field_value
from utilities.forms.fields import ( from utilities.forms.fields import (
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
) )
from utilities.forms.mixins import DistanceValidationMixin
from utilities.forms.rendering import FieldSet, InlineFields from utilities.forms.rendering import FieldSet, InlineFields
from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions
from utilities.templatetags.builtins.filters import bettertitle from utilities.templatetags.builtins.filters import bettertitle
@ -106,7 +105,7 @@ class CircuitTypeForm(NetBoxModelForm):
] ]
class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm): class CircuitForm(TenancyForm, NetBoxModelForm):
provider = DynamicModelChoiceField( provider = DynamicModelChoiceField(
label=_('Provider'), label=_('Provider'),
queryset=Provider.objects.all(), queryset=Provider.objects.all(),

View File

@ -1,16 +0,0 @@
import strawberry
from circuits.choices import *
__all__ = (
'CircuitStatusEnum',
'CircuitTerminationSideEnum',
'CircuitPriorityEnum',
'VirtualCircuitTerminationRoleEnum',
)
CircuitPriorityEnum = strawberry.enum(CircuitPriorityChoices.as_enum(prefix='priority'))
CircuitStatusEnum = strawberry.enum(CircuitStatusChoices.as_enum('status'))
CircuitTerminationSideEnum = strawberry.enum(CircuitTerminationSideChoices.as_enum(prefix='side'))
VirtualCircuitTerminationRoleEnum = strawberry.enum(VirtualCircuitTerminationRoleChoices.as_enum(prefix='role'))

View File

@ -1,19 +0,0 @@
from dataclasses import dataclass
from typing import Annotated, TYPE_CHECKING
import strawberry
import strawberry_django
from netbox.graphql.filter_mixins import OrganizationalModelFilterMixin
if TYPE_CHECKING:
from netbox.graphql.enums import ColorEnum
__all__ = (
'BaseCircuitTypeFilterMixin',
)
@dataclass
class BaseCircuitTypeFilterMixin(OrganizationalModelFilterMixin):
color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()

View File

@ -1,30 +1,7 @@
from datetime import date
from typing import Annotated, TYPE_CHECKING
import strawberry
import strawberry_django import strawberry_django
from strawberry.scalars import ID
from strawberry_django import FilterLookup, DateFilterLookup
from circuits import models from circuits import filtersets, models
from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from dcim.graphql.filter_mixins import CabledObjectModelFilterMixin
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
from netbox.graphql.filter_mixins import (
DistanceFilterMixin,
ImageAttachmentFilterMixin,
OrganizationalModelFilterMixin,
PrimaryModelFilterMixin,
)
from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
from .filter_mixins import BaseCircuitTypeFilterMixin
if TYPE_CHECKING:
from core.graphql.filters import ContentTypeFilter
from dcim.graphql.filters import InterfaceFilter, LocationFilter, RegionFilter, SiteFilter, SiteGroupFilter
from ipam.graphql.filters import ASNFilter
from netbox.graphql.filter_lookups import IntegerLookup
from .enums import *
__all__ = ( __all__ = (
'CircuitFilter', 'CircuitFilter',
@ -41,184 +18,67 @@ __all__ = (
) )
@strawberry_django.filter_type(models.CircuitTermination, lookups=True) @strawberry_django.filter(models.CircuitTermination, lookups=True)
class CircuitTerminationFilter( @autotype_decorator(filtersets.CircuitTerminationFilterSet)
BaseObjectTypeFilterMixin, class CircuitTerminationFilter(BaseFilterMixin):
CustomFieldsFilterMixin,
TagsFilterMixin,
ChangeLogFilterMixin,
CabledObjectModelFilterMixin,
):
circuit: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
term_side: Annotated['CircuitTerminationSideEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
strawberry_django.filter_field()
)
termination_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
termination_id: ID | None = strawberry_django.filter_field()
port_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
xconnect_id: FilterLookup[str] | None = strawberry_django.filter_field()
pp_info: FilterLookup[str] | None = strawberry_django.filter_field()
description: FilterLookup[str] | None = strawberry_django.filter_field()
# Cached relations
_provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field(name='provider_network')
)
_location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field(name='location')
)
_region: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field(name='region')
)
_site_group: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field(name='site_group')
)
_site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field(name='site')
)
@strawberry_django.filter_type(models.Circuit, lookups=True)
class CircuitFilter(
ContactFilterMixin,
ImageAttachmentFilterMixin,
DistanceFilterMixin,
TenancyFilterMixin,
PrimaryModelFilterMixin
):
cid: FilterLookup[str] | None = strawberry_django.filter_field()
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
provider_id: ID | None = strawberry_django.filter_field()
provider_account: Annotated['ProviderAccountFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
provider_account_id: ID | None = strawberry_django.filter_field()
type: Annotated['CircuitTypeFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
type_id: ID | None = strawberry_django.filter_field()
status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
strawberry_django.filter_field()
)
install_date: DateFilterLookup[date] | None = strawberry_django.filter_field()
termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field()
commit_rate: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
terminations: Annotated['CircuitTerminationFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
@strawberry_django.filter_type(models.CircuitType, lookups=True)
class CircuitTypeFilter(BaseCircuitTypeFilterMixin):
pass pass
@strawberry_django.filter_type(models.CircuitGroup, lookups=True) @strawberry_django.filter(models.Circuit, lookups=True)
class CircuitGroupFilter(TenancyFilterMixin, OrganizationalModelFilterMixin): @autotype_decorator(filtersets.CircuitFilterSet)
class CircuitFilter(BaseFilterMixin):
pass pass
@strawberry_django.filter_type(models.CircuitGroupAssignment, lookups=True) @strawberry_django.filter(models.CircuitType, lookups=True)
class CircuitGroupAssignmentFilter( @autotype_decorator(filtersets.CircuitTypeFilterSet)
BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin class CircuitTypeFilter(BaseFilterMixin):
):
member_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
member_id: ID | None = strawberry_django.filter_field()
group: Annotated['CircuitGroupFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
group_id: ID | None = strawberry_django.filter_field()
priority: Annotated['CircuitPriorityEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
strawberry_django.filter_field()
)
@strawberry_django.filter_type(models.Provider, lookups=True)
class ProviderFilter(ContactFilterMixin, PrimaryModelFilterMixin):
name: FilterLookup[str] | None = strawberry_django.filter_field()
slug: FilterLookup[str] | None = strawberry_django.filter_field()
asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
circuits: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
@strawberry_django.filter_type(models.ProviderAccount, lookups=True)
class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilterMixin):
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
provider_id: ID | None = strawberry_django.filter_field()
account: FilterLookup[str] | None = strawberry_django.filter_field()
name: FilterLookup[str] | None = strawberry_django.filter_field()
@strawberry_django.filter_type(models.ProviderNetwork, lookups=True)
class ProviderNetworkFilter(PrimaryModelFilterMixin):
name: FilterLookup[str] | None = strawberry_django.filter_field()
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
provider_id: ID | None = strawberry_django.filter_field()
service_id: FilterLookup[str] | None = strawberry_django.filter_field()
@strawberry_django.filter_type(models.VirtualCircuitType, lookups=True)
class VirtualCircuitTypeFilter(BaseCircuitTypeFilterMixin):
pass pass
@strawberry_django.filter_type(models.VirtualCircuit, lookups=True) @strawberry_django.filter(models.CircuitGroup, lookups=True)
class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilterMixin): @autotype_decorator(filtersets.CircuitGroupFilterSet)
cid: FilterLookup[str] | None = strawberry_django.filter_field() class CircuitGroupFilter(BaseFilterMixin):
provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( pass
strawberry_django.filter_field()
)
provider_network_id: ID | None = strawberry_django.filter_field()
provider_account: Annotated['ProviderAccountFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
provider_account_id: ID | None = strawberry_django.filter_field()
type: Annotated['VirtualCircuitTypeFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
type_id: ID | None = strawberry_django.filter_field()
status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
strawberry_django.filter_field()
)
group_assignments: Annotated['CircuitGroupAssignmentFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
@strawberry_django.filter_type(models.VirtualCircuitTermination, lookups=True) @strawberry_django.filter(models.CircuitGroupAssignment, lookups=True)
class VirtualCircuitTerminationFilter( @autotype_decorator(filtersets.CircuitGroupAssignmentFilterSet)
BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin class CircuitGroupAssignmentFilter(BaseFilterMixin):
): pass
virtual_circuit: Annotated['VirtualCircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
) @strawberry_django.filter(models.Provider, lookups=True)
virtual_circuit_id: ID | None = strawberry_django.filter_field() @autotype_decorator(filtersets.ProviderFilterSet)
role: Annotated['VirtualCircuitTerminationRoleEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( class ProviderFilter(BaseFilterMixin):
strawberry_django.filter_field() pass
)
interface: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field() @strawberry_django.filter(models.ProviderAccount, lookups=True)
) @autotype_decorator(filtersets.ProviderAccountFilterSet)
interface_id: ID | None = strawberry_django.filter_field() class ProviderAccountFilter(BaseFilterMixin):
description: FilterLookup[str] | None = strawberry_django.filter_field() pass
@strawberry_django.filter(models.ProviderNetwork, lookups=True)
@autotype_decorator(filtersets.ProviderNetworkFilterSet)
class ProviderNetworkFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualCircuitType, lookups=True)
@autotype_decorator(filtersets.VirtualCircuitTypeFilterSet)
class VirtualCircuitTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualCircuit, lookups=True)
@autotype_decorator(filtersets.VirtualCircuitFilterSet)
class VirtualCircuitFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualCircuitTermination, lookups=True)
@autotype_decorator(filtersets.VirtualCircuitTerminationFilterSet)
class VirtualCircuitTerminationFilter(BaseFilterMixin):
pass

View File

@ -1,4 +1,4 @@
from typing import Annotated, List, TYPE_CHECKING, Union from typing import Annotated, List, Union
import strawberry import strawberry
import strawberry_django import strawberry_django
@ -10,15 +10,11 @@ from netbox.graphql.types import BaseObjectType, NetBoxObjectType, ObjectType, O
from tenancy.graphql.types import TenantType from tenancy.graphql.types import TenantType
from .filters import * from .filters import *
if TYPE_CHECKING:
from dcim.graphql.types import InterfaceType, LocationType, RegionType, SiteGroupType, SiteType
from ipam.graphql.types import ASNType
__all__ = ( __all__ = (
'CircuitGroupAssignmentType',
'CircuitGroupType',
'CircuitTerminationType', 'CircuitTerminationType',
'CircuitType', 'CircuitType',
'CircuitGroupAssignmentType',
'CircuitGroupType',
'CircuitTypeType', 'CircuitTypeType',
'ProviderType', 'ProviderType',
'ProviderAccountType', 'ProviderAccountType',
@ -32,8 +28,7 @@ __all__ = (
@strawberry_django.type( @strawberry_django.type(
models.Provider, models.Provider,
fields='__all__', fields='__all__',
filters=ProviderFilter, filters=ProviderFilter
pagination=True
) )
class ProviderType(NetBoxObjectType, ContactsMixin): class ProviderType(NetBoxObjectType, ContactsMixin):
@ -46,8 +41,7 @@ class ProviderType(NetBoxObjectType, ContactsMixin):
@strawberry_django.type( @strawberry_django.type(
models.ProviderAccount, models.ProviderAccount,
fields='__all__', fields='__all__',
filters=ProviderAccountFilter, filters=ProviderAccountFilter
pagination=True
) )
class ProviderAccountType(ContactsMixin, NetBoxObjectType): class ProviderAccountType(ContactsMixin, NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
@ -58,8 +52,7 @@ class ProviderAccountType(ContactsMixin, NetBoxObjectType):
@strawberry_django.type( @strawberry_django.type(
models.ProviderNetwork, models.ProviderNetwork,
fields='__all__', fields='__all__',
filters=ProviderNetworkFilter, filters=ProviderNetworkFilter
pagination=True
) )
class ProviderNetworkType(NetBoxObjectType): class ProviderNetworkType(NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
@ -69,9 +62,8 @@ class ProviderNetworkType(NetBoxObjectType):
@strawberry_django.type( @strawberry_django.type(
models.CircuitTermination, models.CircuitTermination,
exclude=['termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'], exclude=('termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'),
filters=CircuitTerminationFilter, filters=CircuitTerminationFilter
pagination=True
) )
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType): class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')] circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]
@ -90,8 +82,7 @@ class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, Ob
@strawberry_django.type( @strawberry_django.type(
models.CircuitType, models.CircuitType,
fields='__all__', fields='__all__',
filters=CircuitTypeFilter, filters=CircuitTypeFilter
pagination=True
) )
class CircuitTypeType(OrganizationalObjectType): class CircuitTypeType(OrganizationalObjectType):
color: str color: str
@ -102,8 +93,7 @@ class CircuitTypeType(OrganizationalObjectType):
@strawberry_django.type( @strawberry_django.type(
models.Circuit, models.Circuit,
fields='__all__', fields='__all__',
filters=CircuitFilter, filters=CircuitFilter
pagination=True
) )
class CircuitType(NetBoxObjectType, ContactsMixin): class CircuitType(NetBoxObjectType, ContactsMixin):
provider: ProviderType provider: ProviderType
@ -119,8 +109,7 @@ class CircuitType(NetBoxObjectType, ContactsMixin):
@strawberry_django.type( @strawberry_django.type(
models.CircuitGroup, models.CircuitGroup,
fields='__all__', fields='__all__',
filters=CircuitGroupFilter, filters=CircuitGroupFilter
pagination=True
) )
class CircuitGroupType(OrganizationalObjectType): class CircuitGroupType(OrganizationalObjectType):
tenant: TenantType | None tenant: TenantType | None
@ -128,9 +117,8 @@ class CircuitGroupType(OrganizationalObjectType):
@strawberry_django.type( @strawberry_django.type(
models.CircuitGroupAssignment, models.CircuitGroupAssignment,
exclude=['member_type', 'member_id'], exclude=('member_type', 'member_id'),
filters=CircuitGroupAssignmentFilter, filters=CircuitGroupAssignmentFilter
pagination=True
) )
class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): class CircuitGroupAssignmentType(TagsMixin, BaseObjectType):
group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')] group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')]
@ -146,8 +134,7 @@ class CircuitGroupAssignmentType(TagsMixin, BaseObjectType):
@strawberry_django.type( @strawberry_django.type(
models.VirtualCircuitType, models.VirtualCircuitType,
fields='__all__', fields='__all__',
filters=VirtualCircuitTypeFilter, filters=VirtualCircuitTypeFilter
pagination=True
) )
class VirtualCircuitTypeType(OrganizationalObjectType): class VirtualCircuitTypeType(OrganizationalObjectType):
color: str color: str
@ -158,8 +145,7 @@ class VirtualCircuitTypeType(OrganizationalObjectType):
@strawberry_django.type( @strawberry_django.type(
models.VirtualCircuitTermination, models.VirtualCircuitTermination,
fields='__all__', fields='__all__',
filters=VirtualCircuitTerminationFilter, filters=VirtualCircuitTerminationFilter
pagination=True
) )
class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
virtual_circuit: Annotated[ virtual_circuit: Annotated[
@ -175,8 +161,7 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
@strawberry_django.type( @strawberry_django.type(
models.VirtualCircuit, models.VirtualCircuit,
fields='__all__', fields='__all__',
filters=VirtualCircuitFilter, filters=VirtualCircuitFilter
pagination=True
) )
class VirtualCircuitType(NetBoxObjectType): class VirtualCircuitType(NetBoxObjectType):
provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"]) provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"])

View File

@ -5,11 +5,11 @@ import taggit.managers
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('dcim', '0001_squashed'), ('dcim', '0001_initial'),
('contenttypes', '0002_remove_content_type_name'), ('contenttypes', '0002_remove_content_type_name'),
('circuits', '0001_squashed'), ('circuits', '0001_initial'),
('extras', '0001_squashed'), ('extras', '0001_initial'),
('tenancy', '0001_squashed_0012'), ('tenancy', '0001_initial'),
] ]
replaces = [ replaces = [

View File

@ -15,8 +15,8 @@ class Migration(migrations.Migration):
] ]
dependencies = [ dependencies = [
('circuits', '0003_squashed_0037'), ('circuits', '0037_new_cabling_models'),
('dcim', '0160_squashed_0166'), ('dcim', '0160_populate_cable_ends'),
] ]
operations = [ operations = [

View File

@ -6,7 +6,7 @@ import utilities.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('circuits', '0038_squashed_0042'), ('circuits', '0042_provideraccount'),
] ]
operations = [ operations = [

View File

@ -8,11 +8,10 @@ def set_null_values(apps, schema_editor):
Circuit = apps.get_model('circuits', 'Circuit') Circuit = apps.get_model('circuits', 'Circuit')
CircuitGroupAssignment = apps.get_model('circuits', 'CircuitGroupAssignment') CircuitGroupAssignment = apps.get_model('circuits', 'CircuitGroupAssignment')
CircuitTermination = apps.get_model('circuits', 'CircuitTermination') CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
db_alias = schema_editor.connection.alias
Circuit.objects.using(db_alias).filter(distance_unit='').update(distance_unit=None) Circuit.objects.filter(distance_unit='').update(distance_unit=None)
CircuitGroupAssignment.objects.using(db_alias).filter(priority='').update(priority=None) CircuitGroupAssignment.objects.filter(priority='').update(priority=None)
CircuitTermination.objects.using(db_alias).filter(cable_end='').update(cable_end=None) CircuitTermination.objects.filter(cable_end='').update(cable_end=None)
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -1,5 +1,4 @@
import django.db.models.deletion import django.db.models.deletion
from django.contrib.contenttypes.models import ContentType
from django.db import migrations, models from django.db import migrations, models
@ -9,15 +8,14 @@ def copy_site_assignments(apps, schema_editor):
""" """
ContentType = apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')
CircuitTermination = apps.get_model('circuits', 'CircuitTermination') CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
ProviderNetwork = apps.get_model('circuits', 'ProviderNetwork')
Site = apps.get_model('dcim', 'Site') Site = apps.get_model('dcim', 'Site')
db_alias = schema_editor.connection.alias
CircuitTermination.objects.using(db_alias).filter(site__isnull=False).update( CircuitTermination.objects.filter(site__isnull=False).update(
termination_type=ContentType.objects.get_for_model(Site), termination_id=models.F('site_id') termination_type=ContentType.objects.get_for_model(Site), termination_id=models.F('site_id')
) )
CircuitTermination.objects.using(db_alias).filter(provider_network__isnull=False).update( ProviderNetwork = apps.get_model('circuits', 'ProviderNetwork')
CircuitTermination.objects.filter(provider_network__isnull=False).update(
termination_type=ContentType.objects.get_for_model(ProviderNetwork), termination_type=ContentType.objects.get_for_model(ProviderNetwork),
termination_id=models.F('provider_network_id'), termination_id=models.F('provider_network_id'),
) )
@ -41,6 +39,9 @@ class Migration(migrations.Migration):
name='termination_type', name='termination_type',
field=models.ForeignKey( field=models.ForeignKey(
blank=True, blank=True,
limit_choices_to=models.Q(
('model__in', ('region', 'sitegroup', 'site', 'location', 'providernetwork'))
),
null=True, null=True,
on_delete=django.db.models.deletion.PROTECT, on_delete=django.db.models.deletion.PROTECT,
related_name='+', related_name='+',
@ -50,26 +51,3 @@ class Migration(migrations.Migration):
# Copy over existing site assignments # Copy over existing site assignments
migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop), migrations.RunPython(code=copy_site_assignments, reverse_code=migrations.RunPython.noop),
] ]
def oc_circuittermination_termination(objectchange, reverting):
site_ct = ContentType.objects.get_by_natural_key('dcim', 'site').pk
provider_network_ct = ContentType.objects.get_by_natural_key('circuits', 'providernetwork').pk
for data in (objectchange.prechange_data, objectchange.postchange_data):
if data is None:
continue
if site_id := data.get('site'):
data.update({
'termination_type': site_ct,
'termination_id': site_id,
})
elif provider_network_id := data.get('provider_network'):
data.update({
'termination_type': provider_network_ct,
'termination_id': provider_network_id,
})
objectchange_migrators = {
'circuits.circuittermination': oc_circuittermination_termination,
}

View File

@ -7,20 +7,15 @@ def populate_denormalized_fields(apps, schema_editor):
Copy site ForeignKey values to the Termination GFK. Copy site ForeignKey values to the Termination GFK.
""" """
CircuitTermination = apps.get_model('circuits', 'CircuitTermination') CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
db_alias = schema_editor.connection.alias
terminations = CircuitTermination.objects.using(db_alias).filter(site__isnull=False).prefetch_related('site') terminations = CircuitTermination.objects.filter(site__isnull=False).prefetch_related('site')
for termination in terminations: for termination in terminations:
termination._region_id = termination.site.region_id termination._region_id = termination.site.region_id
termination._site_group_id = termination.site.group_id termination._site_group_id = termination.site.group_id
termination._site_id = termination.site_id termination._site_id = termination.site_id
# Note: Location cannot be set prior to migration # Note: Location cannot be set prior to migration
CircuitTermination.objects.using(db_alias).bulk_update( CircuitTermination.objects.bulk_update(terminations, ['_region', '_site_group', '_site'], batch_size=100)
terminations,
['_region', '_site_group', '_site'],
batch_size=100
)
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -86,15 +81,3 @@ class Migration(migrations.Migration):
new_name='_provider_network', new_name='_provider_network',
), ),
] ]
def oc_circuittermination_remove_fields(objectchange, reverting):
for data in (objectchange.prechange_data, objectchange.postchange_data):
if data is not None:
data.pop('site', None)
data.pop('provider_network', None)
objectchange_migrators = {
'circuits.circuittermination': oc_circuittermination_remove_fields,
}

View File

@ -1,5 +1,4 @@
import django.db.models.deletion import django.db.models.deletion
from django.contrib.contenttypes.models import ContentType
from django.db import migrations, models from django.db import migrations, models
@ -10,9 +9,8 @@ def set_member_type(apps, schema_editor):
ContentType = apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')
Circuit = apps.get_model('circuits', 'Circuit') Circuit = apps.get_model('circuits', 'Circuit')
CircuitGroupAssignment = apps.get_model('circuits', 'CircuitGroupAssignment') CircuitGroupAssignment = apps.get_model('circuits', 'CircuitGroupAssignment')
db_alias = schema_editor.connection.alias
CircuitGroupAssignment.objects.using(db_alias).update( CircuitGroupAssignment.objects.update(
member_type=ContentType.objects.get_for_model(Circuit) member_type=ContentType.objects.get_for_model(Circuit)
) )
@ -53,6 +51,7 @@ class Migration(migrations.Migration):
name='member_type', name='member_type',
field=models.ForeignKey( field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT, on_delete=django.db.models.deletion.PROTECT,
limit_choices_to=models.Q(('app_label', 'circuits'), ('model__in', ['circuit', 'virtualcircuit'])),
related_name='+', related_name='+',
to='contenttypes.contenttype', to='contenttypes.contenttype',
blank=True, blank=True,
@ -69,6 +68,7 @@ class Migration(migrations.Migration):
model_name='circuitgroupassignment', model_name='circuitgroupassignment',
name='member_type', name='member_type',
field=models.ForeignKey( field=models.ForeignKey(
limit_choices_to=models.Q(('app_label', 'circuits'), ('model__in', ['circuit', 'virtualcircuit'])),
on_delete=django.db.models.deletion.PROTECT, on_delete=django.db.models.deletion.PROTECT,
related_name='+', related_name='+',
to='contenttypes.contenttype' to='contenttypes.contenttype'
@ -83,21 +83,3 @@ class Migration(migrations.Migration):
), ),
), ),
] ]
def oc_circuitgroupassignment_member(objectchange, reverting):
circuit_ct = ContentType.objects.get_by_natural_key('circuits', 'circuit').pk
for data in (objectchange.prechange_data, objectchange.postchange_data):
if data is None:
continue
if circuit_id := data.get('circuit'):
data.update({
'member_type': circuit_ct,
'member_id': circuit_id,
})
data.pop('circuit', None)
objectchange_migrators = {
'circuits.circuitgroupassignment': oc_circuitgroupassignment_member,
}

View File

@ -1,16 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0051_virtualcircuit_group_assignment'),
]
operations = [
migrations.AlterField(
model_name='circuit',
name='_abs_distance',
field=models.DecimalField(blank=True, decimal_places=4, max_digits=13, null=True),
),
]

View File

@ -182,6 +182,7 @@ class CircuitGroupAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin,
""" """
member_type = models.ForeignKey( member_type = models.ForeignKey(
to='contenttypes.ContentType', to='contenttypes.ContentType',
limit_choices_to=CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='+' related_name='+'
) )
@ -248,6 +249,7 @@ class CircuitTermination(
termination_type = models.ForeignKey( termination_type = models.ForeignKey(
to='contenttypes.ContentType', to='contenttypes.ContentType',
on_delete=models.PROTECT, on_delete=models.PROTECT,
limit_choices_to=Q(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
related_name='+', related_name='+',
blank=True, blank=True,
null=True null=True

View File

@ -120,8 +120,7 @@ class CircuitTerminationTable(NetBoxTable):
) )
termination = tables.Column( termination = tables.Column(
verbose_name=_('Termination Point'), verbose_name=_('Termination Point'),
linkify=True, linkify=True
orderable=False,
) )
# Termination types # Termination types
@ -133,7 +132,7 @@ class CircuitTerminationTable(NetBoxTable):
site_group = tables.Column( site_group = tables.Column(
verbose_name=_('Site Group'), verbose_name=_('Site Group'),
linkify=True, linkify=True,
accessor='_site_group' accessor='_sitegroup'
) )
region = tables.Column( region = tables.Column(
verbose_name=_('Region'), verbose_name=_('Region'),

View File

@ -54,8 +54,9 @@ class VirtualCircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable)
linkify=True, linkify=True,
verbose_name=_('Account') verbose_name=_('Account')
) )
type = columns.ColoredLabelColumn( type = tables.Column(
verbose_name=_('Type'), verbose_name=_('Type'),
linkify=True
) )
status = columns.ChoiceFieldColumn() status = columns.ChoiceFieldColumn()
termination_count = columns.LinkedCountColumn( termination_count = columns.LinkedCountColumn(

View File

@ -1,23 +0,0 @@
from django.test import RequestFactory, tag, TestCase
from circuits.models import CircuitTermination
from circuits.tables import CircuitTerminationTable
@tag('regression')
class CircuitTerminationTableTest(TestCase):
def test_every_orderable_field_does_not_throw_exception(self):
terminations = CircuitTermination.objects.all()
disallowed = {'actions', }
orderable_columns = [
column.name for column in CircuitTerminationTable(terminations).columns
if column.orderable and column.name not in disallowed
]
fake_request = RequestFactory().get("/")
for col in orderable_columns:
for dir in ('-', ''):
table = CircuitTerminationTable(terminations)
table.order_by = f'{dir}{col}'
table.as_html(fake_request)

View File

@ -1,11 +1,12 @@
from django.contrib import messages from django.contrib import messages
from django.db import router, transaction from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from dcim.views import PathTraceView from dcim.views import PathTraceView
from ipam.models import ASN from ipam.models import ASN
from netbox.views import generic from netbox.views import generic
from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.query import count_related from utilities.query import count_related
from utilities.views import GetRelatedModelsMixin, register_model_view from utilities.views import GetRelatedModelsMixin, register_model_view
@ -88,6 +89,11 @@ class ProviderBulkDeleteView(generic.BulkDeleteView):
table = tables.ProviderTable table = tables.ProviderTable
@register_model_view(Provider, 'contacts')
class ProviderContactsView(ObjectContactsView):
queryset = Provider.objects.all()
# #
# ProviderAccounts # ProviderAccounts
# #
@ -150,6 +156,11 @@ class ProviderAccountBulkDeleteView(generic.BulkDeleteView):
table = tables.ProviderAccountTable table = tables.ProviderAccountTable
@register_model_view(ProviderAccount, 'contacts')
class ProviderAccountContactsView(ObjectContactsView):
queryset = ProviderAccount.objects.all()
# #
# Provider networks # Provider networks
# #
@ -384,7 +395,7 @@ class CircuitSwapTerminations(generic.ObjectEditView):
if termination_a and termination_z: if termination_a and termination_z:
# Use a placeholder to avoid an IntegrityError on the (circuit, term_side) unique constraint # Use a placeholder to avoid an IntegrityError on the (circuit, term_side) unique constraint
with transaction.atomic(using=router.db_for_write(CircuitTermination)): with transaction.atomic():
termination_a.term_side = '_' termination_a.term_side = '_'
termination_a.save() termination_a.save()
termination_z.term_side = 'A' termination_z.term_side = 'A'
@ -422,6 +433,11 @@ class CircuitSwapTerminations(generic.ObjectEditView):
}) })
@register_model_view(Circuit, 'contacts')
class CircuitContactsView(ObjectContactsView):
queryset = Circuit.objects.all()
# #
# Circuit terminations # Circuit terminations
# #

View File

@ -26,8 +26,8 @@ class DataSourceSerializer(NetBoxModelSerializer):
model = DataSource model = DataSource
fields = [ fields = [
'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description',
'sync_interval', 'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated', 'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated', 'last_synced',
'last_synced', 'file_count', 'file_count',
] ]
brief_fields = ('id', 'url', 'display', 'name', 'description') brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -22,7 +22,6 @@ class CoreConfig(AppConfig):
def ready(self): def ready(self):
from core.api import schema # noqa: F401 from core.api import schema # noqa: F401
from core.checks import check_duplicate_indexes # noqa: F401
from netbox.models.features import register_models from netbox.models.features import register_models
from . import data_backends, events, search # noqa: F401 from . import data_backends, events, search # noqa: F401
from netbox import context_managers # noqa: F401 from netbox import context_managers # noqa: F401

View File

@ -1,41 +0,0 @@
from django.core.checks import Error, register, Tags
from django.db.models import Index, UniqueConstraint
from django.apps import apps
__all__ = (
'check_duplicate_indexes',
)
@register(Tags.models)
def check_duplicate_indexes(app_configs, **kwargs):
"""
Check for an index which is redundant to a declared unique constraint.
"""
errors = []
for model in apps.get_models():
if not (meta := getattr(model, "_meta", None)):
continue
index_fields = {
tuple(index.fields) for index in getattr(meta, 'indexes', [])
if isinstance(index, Index)
}
constraint_fields = {
tuple(constraint.fields) for constraint in getattr(meta, 'constraints', [])
if isinstance(constraint, UniqueConstraint)
}
# Find overlapping definitions
if duplicated := index_fields & constraint_fields:
for fields in duplicated:
errors.append(
Error(
f"Model '{model.__name__}' defines the same field set {fields} in both `Meta.indexes` and "
f"`Meta.constraints`.",
obj=model,
)
)
return errors

View File

@ -7,13 +7,13 @@ from pathlib import Path
from urllib.parse import urlparse from urllib.parse import urlparse
from django import forms from django import forms
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from netbox.data_backends import DataBackend from netbox.data_backends import DataBackend
from netbox.utils import register_data_backend from netbox.utils import register_data_backend
from utilities.constants import HTTP_PROXY_SUPPORTED_SCHEMAS, HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS from utilities.constants import HTTP_PROXY_SUPPORTED_SCHEMAS, HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS
from utilities.proxy import resolve_proxies
from utilities.socks import ProxyPoolManager from utilities.socks import ProxyPoolManager
from .exceptions import SyncError from .exceptions import SyncError
@ -70,18 +70,18 @@ class GitBackend(DataBackend):
# Initialize backend config # Initialize backend config
config = ConfigDict() config = ConfigDict()
self.socks_proxy = None self.use_socks = False
# Apply HTTP proxy (if configured) # Apply HTTP proxy (if configured)
proxies = resolve_proxies(url=self.url, context={'client': self}) or {} if settings.HTTP_PROXIES:
if proxy := proxies.get(self.url_scheme): if proxy := settings.HTTP_PROXIES.get(self.url_scheme, None):
if urlparse(proxy).scheme not in HTTP_PROXY_SUPPORTED_SCHEMAS: if urlparse(proxy).scheme not in HTTP_PROXY_SUPPORTED_SCHEMAS:
raise ImproperlyConfigured(f"Unsupported Git DataSource proxy scheme: {urlparse(proxy).scheme}") raise ImproperlyConfigured(f"Unsupported Git DataSource proxy scheme: {urlparse(proxy).scheme}")
if self.url_scheme in ('http', 'https'): if self.url_scheme in ('http', 'https'):
config.set("http", "proxy", proxy) config.set("http", "proxy", proxy)
if urlparse(proxy).scheme in HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS: if urlparse(proxy).scheme in HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS:
self.socks_proxy = proxy self.use_socks = True
return config return config
@ -98,8 +98,8 @@ class GitBackend(DataBackend):
} }
# check if using socks for proxy - if so need to use custom pool_manager # check if using socks for proxy - if so need to use custom pool_manager
if self.socks_proxy: if self.use_socks:
clone_args['pool_manager'] = ProxyPoolManager(self.socks_proxy) clone_args['pool_manager'] = ProxyPoolManager(settings.HTTP_PROXIES.get(self.url_scheme))
if self.url_scheme in ('http', 'https'): if self.url_scheme in ('http', 'https'):
if self.params.get('username'): if self.params.get('username'):
@ -147,7 +147,7 @@ class S3Backend(DataBackend):
# Initialize backend config # Initialize backend config
return Boto3Config( return Boto3Config(
proxies=resolve_proxies(url=self.url, context={'client': self}), proxies=settings.HTTP_PROXIES,
) )
@contextmanager @contextmanager

View File

@ -1,19 +1,2 @@
from django.core.exceptions import ImproperlyConfigured
__all__ = (
'IncompatiblePluginError',
'JobFailed',
'SyncError',
)
class IncompatiblePluginError(ImproperlyConfigured):
pass
class JobFailed(Exception):
pass
class SyncError(Exception): class SyncError(Exception):
pass pass

View File

@ -29,10 +29,6 @@ class DataSourceFilterSet(NetBoxModelFilterSet):
choices=DataSourceStatusChoices, choices=DataSourceStatusChoices,
null_value=None null_value=None
) )
sync_interval = django_filters.MultipleChoiceFilter(
choices=JobIntervalChoices,
null_value=None
)
class Meta: class Meta:
model = DataSource model = DataSource

View File

@ -1,7 +1,6 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.choices import JobIntervalChoices
from core.models import * from core.models import *
from netbox.forms import NetBoxModelBulkEditForm from netbox.forms import NetBoxModelBulkEditForm
from netbox.utils import get_data_backend_choices from netbox.utils import get_data_backend_choices
@ -30,11 +29,6 @@ class DataSourceBulkEditForm(NetBoxModelBulkEditForm):
max_length=200, max_length=200,
required=False required=False
) )
sync_interval = forms.ChoiceField(
choices=JobIntervalChoices,
required=False,
label=_('Sync interval')
)
comments = CommentField() comments = CommentField()
parameters = forms.JSONField( parameters = forms.JSONField(
label=_('Parameters'), label=_('Parameters'),
@ -48,8 +42,8 @@ class DataSourceBulkEditForm(NetBoxModelBulkEditForm):
model = DataSource model = DataSource
fieldsets = ( fieldsets = (
FieldSet('type', 'enabled', 'description', 'sync_interval', 'parameters', 'ignore_rules', 'comments'), FieldSet('type', 'enabled', 'description', 'comments', 'parameters', 'ignore_rules'),
) )
nullable_fields = ( nullable_fields = (
'description', 'description', 'sync_interval', 'parameters', 'parameters', 'ignore_rules' 'comments', 'description', 'description', 'parameters', 'comments', 'parameters', 'ignore_rules',
) )

View File

@ -11,6 +11,5 @@ class DataSourceImportForm(NetBoxModelImportForm):
class Meta: class Meta:
model = DataSource model = DataSource
fields = ( fields = (
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'parameters', 'ignore_rules', 'name', 'type', 'source_url', 'enabled', 'description', 'comments', 'parameters', 'ignore_rules',
'comments',
) )

View File

@ -27,7 +27,7 @@ class DataSourceFilterForm(NetBoxModelFilterSetForm):
model = DataSource model = DataSource
fieldsets = ( fieldsets = (
FieldSet('q', 'filter_id'), FieldSet('q', 'filter_id'),
FieldSet('type', 'status', 'enabled', 'sync_interval', name=_('Data Source')), FieldSet('type', 'status', name=_('Data Source')),
) )
type = forms.MultipleChoiceField( type = forms.MultipleChoiceField(
label=_('Type'), label=_('Type'),
@ -46,11 +46,6 @@ class DataSourceFilterForm(NetBoxModelFilterSetForm):
choices=BOOLEAN_WITH_BLANK_CHOICES choices=BOOLEAN_WITH_BLANK_CHOICES
) )
) )
sync_interval = forms.ChoiceField(
label=_('Sync interval'),
choices=JobIntervalChoices,
required=False
)
class DataFileFilterForm(NetBoxModelFilterSetForm): class DataFileFilterForm(NetBoxModelFilterSetForm):

View File

@ -36,7 +36,7 @@ class DataSourceForm(NetBoxModelForm):
class Meta: class Meta:
model = DataSource model = DataSource
fields = [ fields = [
'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'comments', 'tags', 'name', 'type', 'source_url', 'enabled', 'description', 'comments', 'ignore_rules', 'tags',
] ]
widgets = { widgets = {
'ignore_rules': forms.Textarea( 'ignore_rules': forms.Textarea(
@ -51,10 +51,7 @@ class DataSourceForm(NetBoxModelForm):
@property @property
def fieldsets(self): def fieldsets(self):
fieldsets = [ fieldsets = [
FieldSet( FieldSet('name', 'type', 'source_url', 'enabled', 'description', 'tags', 'ignore_rules', name=_('Source')),
'name', 'type', 'source_url', 'description', 'tags', 'ignore_rules', name=_('Source')
),
FieldSet('enabled', 'sync_interval', name=_('Sync')),
] ]
if self.backend_fields: if self.backend_fields:
fieldsets.append( fieldsets.append(

View File

@ -1,36 +0,0 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Annotated, TYPE_CHECKING
import strawberry
import strawberry_django
from strawberry import ID
from strawberry_django import DatetimeFilterLookup
if TYPE_CHECKING:
from .filters import *
__all__ = (
'BaseFilterMixin',
'BaseObjectTypeFilterMixin',
'ChangeLogFilterMixin',
)
# @strawberry.input
class BaseFilterMixin: ...
@dataclass
class BaseObjectTypeFilterMixin(BaseFilterMixin):
id: ID | None = strawberry.UNSET
@dataclass
class ChangeLogFilterMixin(BaseFilterMixin):
id: ID | None = strawberry.UNSET
changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()

View File

@ -1,89 +1,28 @@
from datetime import datetime
from typing import Annotated, TYPE_CHECKING
import strawberry
import strawberry_django import strawberry_django
from django.contrib.contenttypes.models import ContentType as DjangoContentType
from strawberry.scalars import ID
from strawberry_django import DatetimeFilterLookup, FilterLookup
from core import models from core import filtersets, models
from core.graphql.filter_mixins import BaseFilterMixin from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from netbox.graphql.filter_mixins import PrimaryModelFilterMixin
if TYPE_CHECKING:
from netbox.graphql.filter_lookups import IntegerLookup, JSONFilter
from users.graphql.filters import UserFilter
__all__ = ( __all__ = (
'DataFileFilter', 'DataFileFilter',
'DataSourceFilter', 'DataSourceFilter',
'ObjectChangeFilter', 'ObjectChangeFilter',
'ContentTypeFilter',
) )
@strawberry_django.filter_type(models.DataFile, lookups=True) @strawberry_django.filter(models.DataFile, lookups=True)
@autotype_decorator(filtersets.DataFileFilterSet)
class DataFileFilter(BaseFilterMixin): class DataFileFilter(BaseFilterMixin):
id: ID | None = strawberry_django.filter_field() pass
created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
source_id: ID | None = strawberry_django.filter_field()
path: FilterLookup[str] | None = strawberry_django.filter_field()
size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
hash: FilterLookup[str] | None = strawberry_django.filter_field()
@strawberry_django.filter_type(models.DataSource, lookups=True) @strawberry_django.filter(models.DataSource, lookups=True)
class DataSourceFilter(PrimaryModelFilterMixin): @autotype_decorator(filtersets.DataSourceFilterSet)
name: FilterLookup[str] | None = strawberry_django.filter_field() class DataSourceFilter(BaseFilterMixin):
type: FilterLookup[str] | None = strawberry_django.filter_field() pass
source_url: FilterLookup[str] | None = strawberry_django.filter_field()
status: FilterLookup[str] | None = strawberry_django.filter_field()
enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
ignore_rules: FilterLookup[str] | None = strawberry_django.filter_field()
parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
last_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
datafiles: Annotated['DataFileFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
@strawberry_django.filter_type(models.ObjectChange, lookups=True) @strawberry_django.filter(models.ObjectChange, lookups=True)
@autotype_decorator(filtersets.ObjectChangeFilterSet)
class ObjectChangeFilter(BaseFilterMixin): class ObjectChangeFilter(BaseFilterMixin):
id: ID | None = strawberry_django.filter_field() pass
time: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
user_name: FilterLookup[str] | None = strawberry_django.filter_field()
request_id: FilterLookup[str] | None = strawberry_django.filter_field()
action: FilterLookup[str] | None = strawberry_django.filter_field()
changed_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
changed_object_type_id: ID | None = strawberry_django.filter_field()
changed_object_id: ID | None = strawberry_django.filter_field()
related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
related_object_id: ID | None = strawberry_django.filter_field()
object_repr: FilterLookup[str] | None = strawberry_django.filter_field()
prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
postchange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
@strawberry_django.filter_type(DjangoContentType, lookups=True)
class ContentTypeFilter(BaseFilterMixin):
id: ID | None = strawberry_django.filter_field()
app_label: FilterLookup[str] | None = strawberry_django.filter_field()
model: FilterLookup[str] | None = strawberry_django.filter_field()

View File

@ -1,4 +1,4 @@
from typing import Annotated, List, TYPE_CHECKING from typing import Annotated, List
import strawberry import strawberry
import strawberry_django import strawberry_django
@ -6,9 +6,6 @@ from django.contrib.contenttypes.models import ContentType
from core.models import ObjectChange from core.models import ObjectChange
if TYPE_CHECKING:
from netbox.core.graphql.types import ObjectChangeType
__all__ = ( __all__ = (
'ChangelogMixin', 'ChangelogMixin',
) )

View File

@ -2,14 +2,12 @@ from typing import Annotated, List
import strawberry import strawberry
import strawberry_django import strawberry_django
from django.contrib.contenttypes.models import ContentType as DjangoContentType
from core import models from core import models
from netbox.graphql.types import BaseObjectType, NetBoxObjectType from netbox.graphql.types import BaseObjectType, NetBoxObjectType
from .filters import * from .filters import *
__all__ = ( __all__ = (
'ContentType',
'DataFileType', 'DataFileType',
'DataSourceType', 'DataSourceType',
'ObjectChangeType', 'ObjectChangeType',
@ -19,8 +17,7 @@ __all__ = (
@strawberry_django.type( @strawberry_django.type(
models.DataFile, models.DataFile,
exclude=['data',], exclude=['data',],
filters=DataFileFilter, filters=DataFileFilter
pagination=True
) )
class DataFileType(BaseObjectType): class DataFileType(BaseObjectType):
source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')]
@ -29,8 +26,7 @@ class DataFileType(BaseObjectType):
@strawberry_django.type( @strawberry_django.type(
models.DataSource, models.DataSource,
fields='__all__', fields='__all__',
filters=DataSourceFilter, filters=DataSourceFilter
pagination=True
) )
class DataSourceType(NetBoxObjectType): class DataSourceType(NetBoxObjectType):
@ -40,17 +36,7 @@ class DataSourceType(NetBoxObjectType):
@strawberry_django.type( @strawberry_django.type(
models.ObjectChange, models.ObjectChange,
fields='__all__', fields='__all__',
filters=ObjectChangeFilter, filters=ObjectChangeFilter
pagination=True
) )
class ObjectChangeType(BaseObjectType): class ObjectChangeType(BaseObjectType):
pass pass
@strawberry_django.type(
DjangoContentType,
fields='__all__',
pagination=True
)
class ContentType:
pass

View File

@ -5,7 +5,6 @@ import sys
from django.conf import settings from django.conf import settings
from netbox.jobs import JobRunner, system_job from netbox.jobs import JobRunner, system_job
from netbox.search.backends import search_backend from netbox.search.backends import search_backend
from utilities.proxy import resolve_proxies
from .choices import DataSourceStatusChoices, JobIntervalChoices from .choices import DataSourceStatusChoices, JobIntervalChoices
from .exceptions import SyncError from .exceptions import SyncError
from .models import DataSource from .models import DataSource
@ -72,7 +71,7 @@ class SystemHousekeepingJob(JobRunner):
url=settings.CENSUS_URL, url=settings.CENSUS_URL,
params=census_data, params=census_data,
timeout=3, timeout=3,
proxies=resolve_proxies(url=settings.CENSUS_URL) proxies=settings.HTTP_PROXIES
) )
except requests.exceptions.RequestException: except requests.exceptions.RequestException:
pass pass

View File

@ -5,7 +5,7 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('core', '0001_squashed_0005'), ('core', '0005_job_created_auto_now'),
] ]
operations = [ operations = [

Some files were not shown because too many files have changed in this diff Show More