diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index b5de9bfee..2d6ca5700 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.2.1 + placeholder: v3.2.2 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 138e0f9b4..13b162741 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.2.1 + placeholder: v3.2.2 validations: required: true - type: dropdown diff --git a/CHANGELOG.md b/CHANGELOG.md index 02d74da64..a6d145951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1 @@ -The changelog has been moved to the [project release notes](https://netbox.readthedocs.io/en/stable/release-notes/). +The changelog has been moved to the [project release notes](https://docs.netbox.dev/en/stable/release-notes/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee69605c7..c01adf4c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -99,7 +99,7 @@ appropriate labels will be applied for categorization. ## Submitting Pull Requests * If you're interested in contributing to NetBox, be sure to check out our -[getting started](https://netbox.readthedocs.io/en/stable/development/getting-started/) +[getting started](https://docs.netbox.dev/en/stable/development/getting-started/) documentation for tips on setting up your development environment. * Be sure to open an issue **before** starting work on a pull request, and @@ -171,7 +171,7 @@ an effort to circumvent the bot: Doing so will not remove the stale label. the understanding that all contributions are submitted under the Apache 2.0 license and that your employer may not make claim to any contributions. Contributions include code work, issue management, and community support. All - development must be in accordance with our [development guidance](https://netbox.readthedocs.io/en/stable/development/). + development must be in accordance with our [development guidance](https://docs.netbox.dev/en/stable/development/). * Maintainers are expected to attend (where feasible) our biweekly ~30-minute sync to review agenda items. This meeting provides opportunity to present and diff --git a/README.md b/README.md index 8429cd4b3..d75c2c1a5 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ NetBox runs as a web application atop the [Django](https://www.djangoproject.com Python framework with a [PostgreSQL](https://www.postgresql.org/) database. For a complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/netbox-community/netbox). -The complete documentation for NetBox can be found at [Read the Docs](https://netbox.readthedocs.io/en/stable/). A public demo instance is available at https://demo.netbox.dev. +The complete documentation for NetBox can be found at [docs.netbox.dev](https://docs.netbox.dev/). A public demo instance is available at https://demo.netbox.dev.

Thank you to our sponsors!

@@ -71,7 +71,7 @@ The complete documentation for NetBox can be found at [Read the Docs](https://ne ### Installation -Please see [the documentation](https://netbox.readthedocs.io/en/stable/) for +Please see [the documentation](https://docs.netbox.dev/) for instructions on installing NetBox. To upgrade NetBox, please download the [latest release](https://github.com/netbox-community/netbox/releases) and run `upgrade.sh`. diff --git a/base_requirements.txt b/base_requirements.txt index 4b814dbc7..095906914 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -68,8 +68,7 @@ gunicorn # Platform-agnostic template rendering engine # https://github.com/pallets/jinja -# Pin to v3.0 for mkdocstrings -Jinja2<3.1 +Jinja2 # Simple markup language for rendering HTML # https://github.com/Python-Markdown/markdown @@ -85,7 +84,7 @@ mkdocs-material # Introspection for embedded code # https://github.com/mkdocstrings/mkdocstrings -mkdocstrings<=0.17.0 +mkdocstrings[python-legacy] # Library for manipulating IP prefixes and addresses # https://github.com/netaddr/netaddr diff --git a/contrib/netbox-rq.service b/contrib/netbox-rq.service index 5b03777ed..6d2f42743 100644 --- a/contrib/netbox-rq.service +++ b/contrib/netbox-rq.service @@ -1,6 +1,6 @@ [Unit] Description=NetBox Request Queue Worker -Documentation=https://netbox.readthedocs.io/en/stable/ +Documentation=https://docs.netbox.dev/ After=network-online.target Wants=network-online.target diff --git a/contrib/netbox.service b/contrib/netbox.service index 18eb0457c..3cd02d988 100644 --- a/contrib/netbox.service +++ b/contrib/netbox.service @@ -1,6 +1,6 @@ [Unit] Description=NetBox WSGI Service -Documentation=https://netbox.readthedocs.io/en/stable/ +Documentation=https://docs.netbox.dev/ After=network-online.target Wants=network-online.target diff --git a/docs/administration/authentication/microsoft-azure-ad.md b/docs/administration/authentication/microsoft-azure-ad.md new file mode 100644 index 000000000..ee24e8232 --- /dev/null +++ b/docs/administration/authentication/microsoft-azure-ad.md @@ -0,0 +1,88 @@ +# Microsoft Azure AD + +This guide explains how to configure single sign-on (SSO) support for NetBox using [Microsoft Azure Active Directory (AD)](https://azure.microsoft.com/en-us/services/active-directory/) as an authentication backend. + +## Azure AD Configuration + +### 1. Create a test user (optional) + +Create a new user in AD to be used for testing. You can skip this step if you already have a suitable account created. + +### 2. Create an app registration + +Under the Azure Active Directory dashboard, navigate to **Add > App registration**. + +![Add an app registration](../../media/authentication/azure_ad_add_app_registration.png) + +Enter a name for the registration (e.g. "NetBox") and ensure that the "single tenant" option is selected. + +Under "Redirect URI", select "Web" for the platform and enter the path to your NetBox installation, ending with `/oauth/complete/azuread-oauth2/`. Note that this URI **must** begin with `https://` unless you are referencing localhost (for development purposes). + +![App registration parameters](../../media/authentication/azure_ad_app_registration.png) + +Once finished, make note of the application (client) ID; this will be used when configuring NetBox. + +![Completed app registration](../../media/authentication/azure_ad_app_registration_created.png) + +!!! tip "Multitenant authentication" + NetBox also supports multitenant authentication via Azure AD, however it requires a different backend and an additional configuration parameter. Please see the [`python-social-auth` documentation](https://python-social-auth.readthedocs.io/en/latest/backends/azuread.html#tenant-support) for details concerning multitenant authentication. + +### 3. Create a secret + +When viewing the newly-created app registration, click the "Add a certificate or secret" link under "Client credentials". Under the "Client secrets" tab, click the "New client secret" button. + +![Add a client secret](../../media/authentication/azure_ad_add_client_secret.png) + +You can optionally specify a description and select a lifetime for the secret. + +![Client secret parameters](../../media/authentication/azure_ad_client_secret.png) + +Once finished, make note of the secret value (not the secret ID); this will be used when configuring NetBox. + +![Client secret parameters](../../media/authentication/azure_ad_client_secret_created.png) + +## NetBox Configuration + +### 1. Enter configuration parameters + +Enter the following configuration parameters in `configuration.py`, substituting your own values: + +```python +REMOTE_AUTH_BACKEND = 'social_core.backends.azuread.AzureADOAuth2' +SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = '{APPLICATION_ID}' +SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = '{SECRET_VALUE}' +``` + +### 2. Restart NetBox + +Restart the NetBox services so that the new configuration takes effect. This is typically done with the command below: + +```no-highlight +sudo systemctl restart netbox +``` + +## Testing + +Log out of NetBox if already authenticated, and click the "Log In" button at top right. You should see the normal login form as well as an option to authenticate using Azure AD. Click that link. + +![NetBox Azure AD login form](../../media/authentication/netbox_azure_ad_login.png) + +You should be redirected to Microsoft's authentication portal. Enter the username/email and password of your test account to continue. You may also be prompted to grant this application access to your account. + +![NetBox Azure AD login form](../../media/authentication/azure_ad_login_portal.png) + +If successful, you will be redirected back to the NetBox UI, and will be logged in as the AD user. You can verify this by navigating to your profile (using the button at top right). + +This user account has been replicated locally to NetBox, and can now be assigned groups and permissions within the NetBox admin UI. + +## Troubleshooting + +### Redirect URI does not Match + +Azure requires that the authenticating client request a redirect URI that matches what you've configured for the app in step two. This URI **must** begin with `https://` (unless using `localhost` for the domain). + +If Azure complains that the requested URI starts with `http://` (not HTTPS), it's likely that your HTTP server is misconfigured or sitting behind a load balancer, so NetBox is not aware that HTTPS is being use. To force the use of an HTTPS redirect URI, set `SOCIAL_AUTH_REDIRECT_IS_HTTPS = True` in `configuration.py` per the [python-social-auth docs](https://python-social-auth.readthedocs.io/en/latest/configuration/settings.html#processing-redirects-and-urlopen). + +### Not Logged in After Authenticating + +If you are redirected to the NetBox UI after authenticating successfully, but are _not_ logged in, double-check the configured backend and app registration. The instructions in this guide pertain only to the `azuread.AzureADOAuth2` backend using a single-tenant app registration. diff --git a/docs/administration/authentication/okta.md b/docs/administration/authentication/okta.md new file mode 100644 index 000000000..ff552d730 --- /dev/null +++ b/docs/administration/authentication/okta.md @@ -0,0 +1,70 @@ +# Okta + +This guide explains how to configure single sign-on (SSO) support for NetBox using [Okta](https://www.okta.com/) as an authentication backend. + +## Okta Configuration + +!!! tip "Okta developer account" + Okta offers free developer accounts at . + +### 1. Create a test user (optional) + +Create a new user in the Okta admin portal to be used for testing. You can skip this step if you already have a suitable account created. + +### 2. Create an app registration + +Within the Okta administration dashboard, navigate to **Applications > Applications**, and click the "Create App Integration" button. Select "OIDC" as the sign-in method, and "Web application" for the application type. + +![Create an app registration](../../media/authentication/okta_create_app_registration.png) + +On the next page, give the app integration a name (e.g. "NetBox") and specify the sign-in and sign-out URIs. These URIs should follow the formats below: + +* Sign-in URI: `https://{netbox}/oauth/complete/okta-openidconnect/` +* Sign-out URI: `https://{netbox}/oauth/disconnect/okta-openidconnect/` + +![Web app integration](../../media/authentication/okta_web_app_integration.png) + +Under "Assignments," select the controlled access setting most appropriate for your organization. Click "Save" to complete the creation. + +Once finished, note the following parameters. These will be used to configured NetBox. + +* Client ID +* Client secret +* Okta domain + +![Okta integration parameters](../../media/authentication/okta_integration_parameters.png) + +## NetBox Configuration + +### 1. Enter configuration parameters + +Enter the following configuration parameters in `configuration.py`, substituting your own values: + +```python +REMOTE_AUTH_BACKEND = 'social_core.backends.okta_openidconnect.OktaOpenIdConnect' +SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY = '{Client ID}' +SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET = '{Client secret}' +SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL = 'https://{Okta domain}/oauth2/' +``` + +### 2. Restart NetBox + +Restart the NetBox services so that the new configuration takes effect. This is typically done with the command below: + +```no-highlight +sudo systemctl restart netbox +``` + +## Testing + +Log out of NetBox if already authenticated, and click the "Log In" button at top right. You should see the normal login form as well as an option to authenticate using Okta. Click that link. + +![NetBox Okta login form](../../media/authentication/netbox_okta_login.png) + +You should be redirected to Okta's authentication portal. Enter the username/email and password of your test account to continue. You may also be prompted to grant this application access to your account. + +![Okta login portal](../../media/authentication/okta_login_portal.png) + +If successful, you will be redirected back to the NetBox UI, and will be logged in as the Okta user. You can verify this by navigating to your profile (using the button at top right). + +This user account has been replicated locally to NetBox, and can now be assigned groups and permissions within the NetBox admin UI. diff --git a/docs/administration/authentication.md b/docs/administration/authentication/overview.md similarity index 91% rename from docs/administration/authentication.md rename to docs/administration/authentication/overview.md index 31983be0b..b405ed09a 100644 --- a/docs/administration/authentication.md +++ b/docs/administration/authentication/overview.md @@ -4,7 +4,7 @@ Local user accounts and groups can be created in NetBox under the "Authentication and Authorization" section of the administrative user interface. This interface is available only to users with the "staff" permission enabled. -At a minimum, each user account must have a username and password set. User accounts may also denote a first name, last name, and email address. [Permissions](./permissions.md) may also be assigned to users and/or groups within the admin UI. +At a minimum, each user account must have a username and password set. User accounts may also denote a first name, last name, and email address. [Permissions](../permissions.md) may also be assigned to users and/or groups within the admin UI. ## Remote Authentication @@ -16,7 +16,7 @@ NetBox may be configured to provide user authenticate via a remote backend in ad REMOTE_AUTH_BACKEND = 'netbox.authentication.LDAPBackend' ``` -NetBox includes an authentication backend which supports LDAP. See the [LDAP installation docs](../installation/6-ldap.md) for more detail about this backend. +NetBox includes an authentication backend which supports LDAP. See the [LDAP installation docs](../../installation/6-ldap.md) for more detail about this backend. ### HTTP Header Authentication diff --git a/docs/customization/custom-validation.md b/docs/customization/custom-validation.md index 9e01f8bb6..f88cd309b 100644 --- a/docs/customization/custom-validation.md +++ b/docs/customization/custom-validation.md @@ -105,11 +105,11 @@ from my_validators import Validator1, Validator2, Validator3 CUSTOM_VALIDATORS = { 'dcim.site': ( - Validator1, - Validator2, + Validator1(), + Validator2(), ), 'dcim.device': ( - Validator3, + Validator3(), ) } ``` diff --git a/docs/installation/4-gunicorn.md b/docs/installation/4-gunicorn.md index 4fc73a58b..21d1f1211 100644 --- a/docs/installation/4-gunicorn.md +++ b/docs/installation/4-gunicorn.md @@ -40,7 +40,7 @@ You should see output similar to the following: ● netbox.service - NetBox WSGI Service Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2021-08-30 04:02:36 UTC; 14h ago - Docs: https://netbox.readthedocs.io/en/stable/ + Docs: https://docs.netbox.dev/ Main PID: 1140492 (gunicorn) Tasks: 19 (limit: 4683) Memory: 666.2M diff --git a/docs/installation/migrating-to-systemd.md b/docs/installation/migrating-to-systemd.md index 51508392f..a71b748fd 100644 --- a/docs/installation/migrating-to-systemd.md +++ b/docs/installation/migrating-to-systemd.md @@ -39,7 +39,7 @@ You can use the command `systemctl status netbox` to verify that the WSGI servic ● netbox.service - NetBox WSGI Service Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2020-10-24 19:23:40 UTC; 25s ago - Docs: https://netbox.readthedocs.io/en/stable/ + Docs: https://docs.netbox.dev/ Main PID: 11993 (gunicorn) Tasks: 6 (limit: 2362) CGroup: /system.slice/netbox.service diff --git a/docs/media/authentication/azure_ad_add_app_registration.png b/docs/media/authentication/azure_ad_add_app_registration.png new file mode 100644 index 000000000..4cc4fc81e Binary files /dev/null and b/docs/media/authentication/azure_ad_add_app_registration.png differ diff --git a/docs/media/authentication/azure_ad_add_client_secret.png b/docs/media/authentication/azure_ad_add_client_secret.png new file mode 100644 index 000000000..93f4eb776 Binary files /dev/null and b/docs/media/authentication/azure_ad_add_client_secret.png differ diff --git a/docs/media/authentication/azure_ad_app_registration.png b/docs/media/authentication/azure_ad_app_registration.png new file mode 100644 index 000000000..eb1634e96 Binary files /dev/null and b/docs/media/authentication/azure_ad_app_registration.png differ diff --git a/docs/media/authentication/azure_ad_app_registration_created.png b/docs/media/authentication/azure_ad_app_registration_created.png new file mode 100644 index 000000000..7e14dc731 Binary files /dev/null and b/docs/media/authentication/azure_ad_app_registration_created.png differ diff --git a/docs/media/authentication/azure_ad_client_secret.png b/docs/media/authentication/azure_ad_client_secret.png new file mode 100644 index 000000000..bebae388b Binary files /dev/null and b/docs/media/authentication/azure_ad_client_secret.png differ diff --git a/docs/media/authentication/azure_ad_client_secret_created.png b/docs/media/authentication/azure_ad_client_secret_created.png new file mode 100644 index 000000000..c142f2a91 Binary files /dev/null and b/docs/media/authentication/azure_ad_client_secret_created.png differ diff --git a/docs/media/authentication/azure_ad_login_portal.png b/docs/media/authentication/azure_ad_login_portal.png new file mode 100644 index 000000000..891f64355 Binary files /dev/null and b/docs/media/authentication/azure_ad_login_portal.png differ diff --git a/docs/media/authentication/netbox_azure_ad_login.png b/docs/media/authentication/netbox_azure_ad_login.png new file mode 100644 index 000000000..a1bbe69d1 Binary files /dev/null and b/docs/media/authentication/netbox_azure_ad_login.png differ diff --git a/docs/media/authentication/netbox_okta_login.png b/docs/media/authentication/netbox_okta_login.png new file mode 100644 index 000000000..34df39cba Binary files /dev/null and b/docs/media/authentication/netbox_okta_login.png differ diff --git a/docs/media/authentication/okta_create_app_registration.png b/docs/media/authentication/okta_create_app_registration.png new file mode 100644 index 000000000..7a62d3d9e Binary files /dev/null and b/docs/media/authentication/okta_create_app_registration.png differ diff --git a/docs/media/authentication/okta_integration_parameters.png b/docs/media/authentication/okta_integration_parameters.png new file mode 100644 index 000000000..b5d7794ec Binary files /dev/null and b/docs/media/authentication/okta_integration_parameters.png differ diff --git a/docs/media/authentication/okta_login_portal.png b/docs/media/authentication/okta_login_portal.png new file mode 100644 index 000000000..48b62d3e0 Binary files /dev/null and b/docs/media/authentication/okta_login_portal.png differ diff --git a/docs/media/authentication/okta_web_app_integration.png b/docs/media/authentication/okta_web_app_integration.png new file mode 100644 index 000000000..6587127c6 Binary files /dev/null and b/docs/media/authentication/okta_web_app_integration.png differ diff --git a/docs/release-notes/version-2.1.md b/docs/release-notes/version-2.1.md index d4804661f..7ec172b1f 100644 --- a/docs/release-notes/version-2.1.md +++ b/docs/release-notes/version-2.1.md @@ -121,7 +121,7 @@ A new API endpoint has been added at `/api/ipam/prefixes//available-ips/`. A #### NAPALM Integration ([#1348](https://github.com/netbox-community/netbox/issues/1348)) -The [NAPALM automation](https://github.com/napalm-automation/napalm) library provides an abstracted interface for pulling live data (e.g. uptime, software version, running config, LLDP neighbors, etc.) from network devices. The NetBox API has been extended to support executing read-only NAPALM methods on devices defined in NetBox. To enable this functionality, ensure that NAPALM has been installed (`pip install napalm`) and the `NETBOX_USERNAME` and `NETBOX_PASSWORD` [configuration parameters](https://netbox.readthedocs.io/en/stable/configuration/optional-settings/#netbox_username) have been set in configuration.py. +The [NAPALM automation](https://github.com/napalm-automation/napalm) library provides an abstracted interface for pulling live data (e.g. uptime, software version, running config, LLDP neighbors, etc.) from network devices. The NetBox API has been extended to support executing read-only NAPALM methods on devices defined in NetBox. To enable this functionality, ensure that NAPALM has been installed (`pip install napalm`) and the `NETBOX_USERNAME` and `NETBOX_PASSWORD` [configuration parameters](https://docs.netbox.dev/en/stable/configuration/optional-settings/#netbox_username) have been set in configuration.py. ### Enhancements diff --git a/docs/release-notes/version-2.2.md b/docs/release-notes/version-2.2.md index e13c4fe69..4f75fb25a 100644 --- a/docs/release-notes/version-2.2.md +++ b/docs/release-notes/version-2.2.md @@ -196,7 +196,7 @@ Our second-most popular feature request has arrived! NetBox now supports the cre #### Custom Validation Reports ([#1511](https://github.com/netbox-community/netbox/issues/1511)) -Users can now create custom reports which are run to validate data in NetBox. Reports work very similar to Python unit tests: Each report inherits from NetBox's Report class and contains one or more test method. Reports can be run and retrieved via the web UI, API, or CLI. See [the docs](https://netbox.readthedocs.io/en/stable/miscellaneous/reports/) for more info. +Users can now create custom reports which are run to validate data in NetBox. Reports work very similar to Python unit tests: Each report inherits from NetBox's Report class and contains one or more test method. Reports can be run and retrieved via the web UI, API, or CLI. See [the docs](https://docs.netbox.dev/en/stable/miscellaneous/reports/) for more info. ### Enhancements diff --git a/docs/release-notes/version-2.5.md b/docs/release-notes/version-2.5.md index 666ecb6f1..01c5bf57c 100644 --- a/docs/release-notes/version-2.5.md +++ b/docs/release-notes/version-2.5.md @@ -295,7 +295,7 @@ This release upgrades the Django framework to version 2.2. #### Python 3 Required -As promised, Python 2 support has been completed removed. Python 3.5 or higher is now required to run NetBox. Please see [our Python 3 migration guide](https://netbox.readthedocs.io/en/stable/installation/migrating-to-python3/) for assistance with upgrading. +As promised, Python 2 support has been completed removed. Python 3.5 or higher is now required to run NetBox. Please see [our Python 3 migration guide](https://docs.netbox.dev/en/stable/installation/migrating-to-python3/) for assistance with upgrading. #### Removed Deprecated User Activity Log diff --git a/docs/release-notes/version-2.6.md b/docs/release-notes/version-2.6.md index 7e9e8fea3..1f56ea889 100644 --- a/docs/release-notes/version-2.6.md +++ b/docs/release-notes/version-2.6.md @@ -218,7 +218,7 @@ #### Custom Scripts ([#3415](https://github.com/netbox-community/netbox/issues/3415)) -Custom scripts allow for the execution of arbitrary code via the NetBox UI. They can be used to automatically create, manipulate, or clean up objects or perform other tasks within NetBox. Scripts are defined as Python files which contain one or more subclasses of `extras.scripts.Script`. Variable fields can be defined within scripts, which render as form fields within the web UI to prompt the user for input data. Scripts are executed and information is logged via the web UI. Please see [the docs](https://netbox.readthedocs.io/en/stable/customization/custom-scripts/) for more detail. +Custom scripts allow for the execution of arbitrary code via the NetBox UI. They can be used to automatically create, manipulate, or clean up objects or perform other tasks within NetBox. Scripts are defined as Python files which contain one or more subclasses of `extras.scripts.Script`. Variable fields can be defined within scripts, which render as form fields within the web UI to prompt the user for input data. Scripts are executed and information is logged via the web UI. Please see [the docs](https://docs.netbox.dev/en/stable/customization/custom-scripts/) for more detail. Note: There are currently no API endpoints for this feature. These are planned for the upcoming v2.7 release. diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index e0297a692..ebc14d63c 100644 --- a/docs/release-notes/version-2.7.md +++ b/docs/release-notes/version-2.7.md @@ -67,7 +67,7 @@ ## v2.7.9 (2020-03-06) -**Note:** This release will deploy a Python virtual environment on upgrade in the `venv/` directory. This will require modifying the paths to your Python and gunicorn executables in the systemd service files. For more detail, please see the [upgrade instructions](https://netbox.readthedocs.io/en/stable/installation/upgrading/). +**Note:** This release will deploy a Python virtual environment on upgrade in the `venv/` directory. This will require modifying the paths to your Python and gunicorn executables in the systemd service files. For more detail, please see the [upgrade instructions](https://docs.netbox.dev/en/stable/installation/upgrading/). ### Enhancements @@ -418,7 +418,7 @@ to another source before upgrading NetBox to v2.7, as any existing topology maps #### Supervisor Replaced with systemd ([#2902](https://github.com/netbox-community/netbox/issues/2902)) -The NetBox [installation documentation](https://netbox.readthedocs.io/en/stable/installation/) has been updated to +The NetBox [installation documentation](https://docs.netbox.dev/en/stable/installation/) has been updated to provide instructions for managing the WSGI and RQ services using systemd instead of supervisor. This removes the need to install supervisor and simplifies administration of the processes. diff --git a/docs/release-notes/version-2.8.md b/docs/release-notes/version-2.8.md index af758f928..ba395793a 100644 --- a/docs/release-notes/version-2.8.md +++ b/docs/release-notes/version-2.8.md @@ -235,14 +235,14 @@ This release introduces support for custom plugins, which can be used to extend * Introduce new API endpoints * Add custom request/response middleware -For NetBox plugins to be recognized, they must be installed and added by name to the `PLUGINS` configuration parameter. (Plugin support is disabled by default.) Plugins can be configured under the `PLUGINS_CONFIG` parameter. More information can be found the in the [plugins documentation](https://netbox.readthedocs.io/en/stable/plugins/). +For NetBox plugins to be recognized, they must be installed and added by name to the `PLUGINS` configuration parameter. (Plugin support is disabled by default.) Plugins can be configured under the `PLUGINS_CONFIG` parameter. More information can be found the in the [plugins documentation](https://docs.netbox.dev/en/stable/plugins/). ### Enhancements * [#1754](https://github.com/netbox-community/netbox/issues/1754) - Added support for nested rack groups * [#3939](https://github.com/netbox-community/netbox/issues/3939) - Added support for nested tenant groups * [#4078](https://github.com/netbox-community/netbox/issues/4078) - Standardized description fields across all models -* [#4195](https://github.com/netbox-community/netbox/issues/4195) - Enabled application logging (see [logging configuration](https://netbox.readthedocs.io/en/stable/configuration/optional-settings/#logging)) +* [#4195](https://github.com/netbox-community/netbox/issues/4195) - Enabled application logging (see [logging configuration](https://docs.netbox.dev/en/stable/configuration/optional-settings/#logging)) ### Bug Fixes diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index cd19a6d65..56f6b7357 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -1,5 +1,32 @@ # NetBox v3.2 +## v3.2.2 (2022-04-28) + +### Enhancements + +* [#9060](https://github.com/netbox-community/netbox/issues/9060) - Add device type filters for device bays, module bays, and inventory items +* [#9152](https://github.com/netbox-community/netbox/issues/9152) - Annotate related object type under custom field view +* [#9192](https://github.com/netbox-community/netbox/issues/9192) - Add Ubiquiti SmartPower connector type +* [#9214](https://github.com/netbox-community/netbox/issues/9214) - Linkify cluster counts in cluster type & group tables + +### Bug Fixes + +* [#4264](https://github.com/netbox-community/netbox/issues/4264) - Treat 0th IP as unusable for IPv6 prefixes (excluding /127s) +* [#8941](https://github.com/netbox-community/netbox/issues/8941) - Fix dynamic dropdown behavior when browser is zoomed +* [#8959](https://github.com/netbox-community/netbox/issues/8959) - Prevent exception when refreshing scripts list (avoid race condition) +* [#9132](https://github.com/netbox-community/netbox/issues/9132) - Limit location options by selected site when creating a wireless link +* [#9133](https://github.com/netbox-community/netbox/issues/9133) - Upgrade script should require Python 3.8 or later +* [#9138](https://github.com/netbox-community/netbox/issues/9138) - Avoid inadvertent form submission when utilizing quick search field on object lists +* [#9151](https://github.com/netbox-community/netbox/issues/9151) - Child prefix counts not annotated on aggregates list under RIR view +* [#9156](https://github.com/netbox-community/netbox/issues/9156) - Fix loading UserConfig data from fixtures +* [#9158](https://github.com/netbox-community/netbox/issues/9158) - Do not list tags field for CSV forms which do not support tag assignment +* [#9194](https://github.com/netbox-community/netbox/issues/9194) - Support position assignment when add module bays to multiple devices +* [#9206](https://github.com/netbox-community/netbox/issues/9206) - Show header for comments field under module & module type creation views +* [#9222](https://github.com/netbox-community/netbox/issues/9222) - Fix circuit ID display under cable view +* [#9227](https://github.com/netbox-community/netbox/issues/9227) - Fix related object assignment when recording change record for interfaces + +--- + ## v3.2.1 (2022-04-14) ### Enhancements diff --git a/mkdocs.yml b/mkdocs.yml index 3af855d3b..225c6d4bf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,6 @@ site_name: NetBox Documentation site_dir: netbox/project-static/docs -site_url: https://netbox.readthedocs.io/ +site_url: https://docs.netbox.dev/ repo_name: netbox-community/netbox repo_url: https://github.com/netbox-community/netbox theme: @@ -19,6 +19,7 @@ theme: icon: material/lightbulb name: Switch to Light Mode plugins: + - search - mkdocstrings: handlers: python: @@ -117,7 +118,10 @@ nav: - GraphQL API: 'plugins/development/graphql-api.md' - Background Tasks: 'plugins/development/background-tasks.md' - Administration: - - Authentication: 'administration/authentication.md' + - Authentication: + - Overview: 'administration/authentication/overview.md' + - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md' + - Okta: 'administration/authentication/okta.md' - Permissions: 'administration/permissions.md' - Housekeeping: 'administration/housekeeping.md' - Replicating NetBox: 'administration/replicating-netbox.md' diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index b0aa1c60c..e369201b4 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -349,6 +349,7 @@ class PowerPortTypeChoices(ChoiceSet): TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32' TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1' TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top' + TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower' # Other TYPE_HARDWIRED = 'hardwired' @@ -464,6 +465,7 @@ class PowerPortTypeChoices(ChoiceSet): (TYPE_NEUTRIK_POWERCON_32A, 'Neutrik powerCON (32A)'), (TYPE_NEUTRIK_POWERCON_TRUE1, 'Neutrik powerCON TRUE1'), (TYPE_NEUTRIK_POWERCON_TRUE1_TOP, 'Neutrik powerCON TRUE1 TOP'), + (TYPE_UBIQUITI_SMARTPOWER, 'Ubiquiti SmartPower'), )), ('Other', ( (TYPE_HARDWIRED, 'Hardwired'), diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 0f4e7cf7e..54f533a7f 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -435,6 +435,10 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet): method='_device_bays', label='Has device bays', ) + inventory_items = django_filters.BooleanFilter( + method='_inventory_items', + label='Has inventory items', + ) class Meta: model = DeviceType @@ -479,6 +483,9 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet): def _device_bays(self, queryset, name, value): return queryset.exclude(devicebaytemplates__isnull=value) + def _inventory_items(self, queryset, name, value): + return queryset.exclude(inventoryitemtemplates__isnull=value) + class ModuleTypeFilterSet(NetBoxModelFilterSet): manufacturer_id = django_filters.ModelMultipleChoiceFilter( diff --git a/netbox/dcim/forms/bulk_create.py b/netbox/dcim/forms/bulk_create.py index 4d73fcc2a..314a7a75f 100644 --- a/netbox/dcim/forms/bulk_create.py +++ b/netbox/dcim/forms/bulk_create.py @@ -3,7 +3,7 @@ from django import forms from dcim.models import * from extras.forms import CustomFieldsMixin from extras.models import Tag -from utilities.forms import DynamicModelMultipleChoiceField, form_from_model +from utilities.forms import DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model from .object_create import ComponentCreateForm __all__ = ( @@ -98,7 +98,13 @@ class RearPortBulkCreateForm( class ModuleBayBulkCreateForm(DeviceBulkAddComponentForm): model = ModuleBay - field_order = ('name_pattern', 'label_pattern', 'description', 'tags') + field_order = ('name_pattern', 'label_pattern', 'position_pattern', 'description', 'tags') + + position_pattern = ExpandableNameField( + label='Position', + required=False, + help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)' + ) class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm): diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index d5335947a..7f30941a2 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -331,7 +331,7 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm): ('Hardware', ('manufacturer_id', 'part_number', 'subdevice_role', 'airflow')), ('Components', ( 'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', - 'pass_through_ports', + 'pass_through_ports', 'device_bays', 'module_bays', 'inventory_items', )), ) manufacturer_id = DynamicModelMultipleChoiceField( @@ -392,6 +392,27 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm): choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + device_bays = forms.NullBooleanField( + required=False, + label='Has device bays', + widget=StaticSelect( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + module_bays = forms.NullBooleanField( + required=False, + label='Has module bays', + widget=StaticSelect( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + inventory_items = forms.NullBooleanField( + required=False, + label='Has inventory items', + widget=StaticSelect( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) tag = TagFilterField(model) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index fe9daf938..31c5b957d 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -385,6 +385,12 @@ class ModuleTypeForm(NetBoxModelForm): ) comments = CommentField() + fieldsets = ( + ('Module Type', ( + 'manufacturer', 'model', 'part_number', 'tags', + )), + ) + class Meta: model = ModuleType fields = [ @@ -627,6 +633,15 @@ class ModuleForm(NetBoxModelForm): help_text="Automatically populate components associated with this module type" ) + fieldsets = ( + ('Module', ( + 'device', 'module_bay', 'manufacturer', 'module_type', 'tags', + )), + ('Hardware', ( + 'serial', 'asset_tag', 'replicate_components', + )), + ) + class Meta: model = Module fields = [ diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 3ed786000..a3b182da1 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -77,7 +77,7 @@ class ComponentModel(NetBoxModel): def to_objectchange(self, action): objectchange = super().to_objectchange(action) objectchange.related_object = self.device - return super().to_objectchange(action) + return objectchange @property def parent_object(self): diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 2e2c3baf7..8480c97bf 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -698,6 +698,9 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceBayTemplate(device_type=device_types[0], name='Device Bay 1'), DeviceBayTemplate(device_type=device_types[1], name='Device Bay 2'), )) + # Assigned DeviceType must have parent subdevice_role + inventory_item = InventoryItemTemplate(device_type=device_types[1], name='Inventory Item 1') + inventory_item.save() def test_model(self): params = {'model': ['Model 1', 'Model 2']} @@ -784,6 +787,12 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'module_bays': 'false'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_inventory_items(self): + params = {'inventory_items': 'true'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + params = {'inventory_items': 'false'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = ModuleType.objects.all() diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 4eacddbeb..4332d72f7 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -5,6 +5,7 @@ import os import pkgutil import sys import traceback +import threading from collections import OrderedDict import yaml @@ -13,11 +14,9 @@ from django.conf import settings from django.core.validators import RegexValidator from django.db import transaction from django.utils.functional import classproperty -from django_rq import job from extras.api.serializers import ScriptOutputSerializer from extras.choices import JobResultStatusChoices, LogLevelChoices -from extras.models import JobResult from ipam.formfields import IPAddressFormField, IPNetworkFormField from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator from utilities.exceptions import AbortTransaction @@ -42,6 +41,8 @@ __all__ = [ 'TextVar', ] +lock = threading.Lock() + # # Script variables @@ -491,11 +492,14 @@ def get_scripts(use_names=False): # Iterate through all modules within the scripts path. These are the user-created files in which reports are # defined. for importer, module_name, _ in pkgutil.iter_modules([settings.SCRIPTS_ROOT]): - # Remove cached module to ensure consistency with filesystem - if module_name in sys.modules: - del sys.modules[module_name] + # Use a lock as removing and loading modules is not thread safe + with lock: + # Remove cached module to ensure consistency with filesystem + if module_name in sys.modules: + del sys.modules[module_name] + + module = importer.find_module(module_name).load_module(module_name) - module = importer.find_module(module_name).load_module(module_name) if use_names and hasattr(module, 'name'): module_name = module.name module_scripts = OrderedDict() diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index 9aec0cff8..a3b8fb2c1 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -507,16 +507,20 @@ class Prefix(GetAvailablePrefixesMixin, NetBoxModel): child_ranges.add(iprange.range) available_ips = prefix - child_ips - child_ranges - # IPv6, pool, or IPv4 /31-/32 sets are fully usable - if self.family == 6 or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31): + # IPv6 /127's, pool, or IPv4 /31-/32 sets are fully usable + if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or (self.family == 4 and self.prefix.prefixlen >= 31): return available_ips - # For "normal" IPv4 prefixes, omit first and last addresses - available_ips -= netaddr.IPSet([ - netaddr.IPAddress(self.prefix.first), - netaddr.IPAddress(self.prefix.last), - ]) - + if self.family == 4: + # For "normal" IPv4 prefixes, omit first and last addresses + available_ips -= netaddr.IPSet([ + netaddr.IPAddress(self.prefix.first), + netaddr.IPAddress(self.prefix.last), + ]) + else: + # For IPv6 prefixes, omit the Subnet-Router anycast address + # per RFC 4291 + available_ips -= netaddr.IPSet([netaddr.IPAddress(self.prefix.first)]) return available_ips def get_first_available_ip(self): diff --git a/netbox/ipam/tests/test_models.py b/netbox/ipam/tests/test_models.py index a664b34f4..09bc95799 100644 --- a/netbox/ipam/tests/test_models.py +++ b/netbox/ipam/tests/test_models.py @@ -185,6 +185,18 @@ class TestPrefix(TestCase): IPAddress.objects.create(address=IPNetwork('10.0.0.4/24')) self.assertEqual(parent_prefix.get_first_available_ip(), '10.0.0.5/24') + def test_get_first_available_ip_ipv6(self): + parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500::/64')) + self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500::1/64') + + def test_get_first_available_ip_ipv6_rfc3627(self): + parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500:4::/126')) + self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500:4::1/126') + + def test_get_first_available_ip_ipv6_rfc6164(self): + parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500:5::/127')) + self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500:5::/127') + def test_get_utilization_container(self): prefixes = ( Prefix(prefix=IPNetwork('10.0.0.0/24'), status=PrefixStatusChoices.STATUS_CONTAINER), diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 41bef2527..57a682c94 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -158,8 +158,8 @@ class RIRView(generic.ObjectView): queryset = RIR.objects.all() def get_extra_context(self, request, instance): - aggregates = Aggregate.objects.restrict(request.user, 'view').filter( - rir=instance + aggregates = Aggregate.objects.restrict(request.user, 'view').filter(rir=instance).annotate( + child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) ) aggregates_table = tables.AggregateTable(aggregates, exclude=('rir', 'utilization')) aggregates_table.configure(request) diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index 6367d6d70..a13e8d192 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -39,6 +39,7 @@ AUTH_BACKEND_ATTRS = { 'keycloak': ('Keycloak', None), 'microsoft-graph': ('Microsoft Graph', 'microsoft'), 'okta': ('Okta', None), + 'okta-openidconnect': ('Okta (OIDC)', None), 'salesforce-oauth2': ('Salesforce', 'salesforce'), } diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index c842c6c06..0e232af1d 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -61,6 +61,8 @@ class NetBoxModelCSVForm(CSVModelForm, NetBoxModelForm): """ Base form for creating a NetBox objects from CSV data. Used for bulk importing. """ + tags = None # Temporary fix in lieu of tag import support (see #9158) + def _get_form_field(self, customfield): return customfield.to_form_field(for_csv_import=True) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 4e3017d8d..7f0735546 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -26,7 +26,7 @@ django.utils.encoding.force_text = force_str # Environment setup # -VERSION = '3.2.1' +VERSION = '3.2.2' # Hostname HOSTNAME = platform.node() diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 1a7581a6c..acd1abbf2 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 3eeaf8b3d..ebf3e0a39 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/select/api/apiSelect.ts b/netbox/project-static/src/select/api/apiSelect.ts index be8a86631..88b35a0e9 100644 --- a/netbox/project-static/src/select/api/apiSelect.ts +++ b/netbox/project-static/src/select/api/apiSelect.ts @@ -570,8 +570,9 @@ export class APISelect { * additional paginated options. */ private handleScroll(): void { + // Floor scrollTop as chrome can return fractions on some zoom levels. const atBottom = - this.slim.slim.list.scrollTop + this.slim.slim.list.offsetHeight === + Math.floor(this.slim.slim.list.scrollTop) + this.slim.slim.list.offsetHeight === this.slim.slim.list.scrollHeight; if (this.atBottom && !atBottom) { diff --git a/netbox/templates/dcim/device/consoleports.html b/netbox/templates/dcim/device/consoleports.html index f96854ca8..afc306bd4 100644 --- a/netbox/templates/dcim/device/consoleports.html +++ b/netbox/templates/dcim/device/consoleports.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsolePortTable_config" %} +
{% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsolePortTable_config" %}
diff --git a/netbox/templates/dcim/device/consoleserverports.html b/netbox/templates/dcim/device/consoleserverports.html index eb27b4ab0..5f244cdc7 100644 --- a/netbox/templates/dcim/device/consoleserverports.html +++ b/netbox/templates/dcim/device/consoleserverports.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsoleServerPortTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsoleServerPortTable_config" %}
diff --git a/netbox/templates/dcim/device/devicebays.html b/netbox/templates/dcim/device/devicebays.html index 672cb192a..5e33bdae0 100644 --- a/netbox/templates/dcim/device/devicebays.html +++ b/netbox/templates/dcim/device/devicebays.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceDeviceBayTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceDeviceBayTable_config" %}
diff --git a/netbox/templates/dcim/device/frontports.html b/netbox/templates/dcim/device/frontports.html index 816d193de..0d0f9577c 100644 --- a/netbox/templates/dcim/device/frontports.html +++ b/netbox/templates/dcim/device/frontports.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceFrontPortTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceFrontPortTable_config" %}
diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index d7f8dff55..22f6d8be5 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -4,44 +4,46 @@ {% load static %} {% block content %} - - {% csrf_token %} -
-
-
- -
-
-
-
- {% if request.user.is_authenticated %} - - {% endif %} - - -
+
+
+
+
+
+
+ {% if request.user.is_authenticated %} + + {% endif %} + + +
+
+
+ + + {% csrf_token %} +
diff --git a/netbox/templates/dcim/device/inventory.html b/netbox/templates/dcim/device/inventory.html index c6452cf78..18a0712f3 100644 --- a/netbox/templates/dcim/device/inventory.html +++ b/netbox/templates/dcim/device/inventory.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceInventoryItemTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceInventoryItemTable_config" %}
diff --git a/netbox/templates/dcim/device/modulebays.html b/netbox/templates/dcim/device/modulebays.html index e9c672b57..fc1c9a60d 100644 --- a/netbox/templates/dcim/device/modulebays.html +++ b/netbox/templates/dcim/device/modulebays.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceModuleBayTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceModuleBayTable_config" %}
diff --git a/netbox/templates/dcim/device/poweroutlets.html b/netbox/templates/dcim/device/poweroutlets.html index 19d8298af..d312fbbd0 100644 --- a/netbox/templates/dcim/device/poweroutlets.html +++ b/netbox/templates/dcim/device/poweroutlets.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerOutletTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerOutletTable_config" %}
diff --git a/netbox/templates/dcim/device/powerports.html b/netbox/templates/dcim/device/powerports.html index 82c088392..cf71e81ba 100644 --- a/netbox/templates/dcim/device/powerports.html +++ b/netbox/templates/dcim/device/powerports.html @@ -4,9 +4,10 @@ {% load static %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerPortTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerPortTable_config" %}
diff --git a/netbox/templates/dcim/device/rearports.html b/netbox/templates/dcim/device/rearports.html index 868def466..73341990f 100644 --- a/netbox/templates/dcim/device/rearports.html +++ b/netbox/templates/dcim/device/rearports.html @@ -4,9 +4,10 @@ {% load helpers %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceRearPortTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceRearPortTable_config" %}
diff --git a/netbox/templates/dcim/inc/cable_termination.html b/netbox/templates/dcim/inc/cable_termination.html index f44c3b9d1..6d75aee85 100644 --- a/netbox/templates/dcim/inc/cable_termination.html +++ b/netbox/templates/dcim/inc/cable_termination.html @@ -32,7 +32,11 @@ Circuit - {{ termination.|linkify }} ({{ termination }}) + {{ termination.circuit|linkify }} + + + Termination + {{ termination }} {% endif %} diff --git a/netbox/templates/extras/customfield.html b/netbox/templates/extras/customfield.html index 9be7a485a..e8c3df460 100644 --- a/netbox/templates/extras/customfield.html +++ b/netbox/templates/extras/customfield.html @@ -21,7 +21,10 @@ Type - {{ object.get_type_display }} + + {{ object.get_type_display }} + {% if object.object_type %}({{ object.object_type.model|bettertitle }}){% endif %} + Description diff --git a/netbox/templates/home.html b/netbox/templates/home.html index 25ccbf181..a12ec9277 100644 --- a/netbox/templates/home.html +++ b/netbox/templates/home.html @@ -13,7 +13,7 @@ NetBox v{{ new_release.version }} is available.
- Upgrade Instructions + Upgrade Instructions
diff --git a/netbox/templates/ipam/aggregate/prefixes.html b/netbox/templates/ipam/aggregate/prefixes.html index bb574ebf0..d1b48429a 100644 --- a/netbox/templates/ipam/aggregate/prefixes.html +++ b/netbox/templates/ipam/aggregate/prefixes.html @@ -12,9 +12,10 @@ {% endblock %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %}
diff --git a/netbox/templates/ipam/iprange/ip_addresses.html b/netbox/templates/ipam/iprange/ip_addresses.html index a13910406..d9ac77fd0 100644 --- a/netbox/templates/ipam/iprange/ip_addresses.html +++ b/netbox/templates/ipam/iprange/ip_addresses.html @@ -10,9 +10,10 @@ {% endblock %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %}
diff --git a/netbox/templates/ipam/prefix/ip_addresses.html b/netbox/templates/ipam/prefix/ip_addresses.html index b26375ebe..d734b825f 100644 --- a/netbox/templates/ipam/prefix/ip_addresses.html +++ b/netbox/templates/ipam/prefix/ip_addresses.html @@ -10,9 +10,10 @@ {% endblock %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %}
diff --git a/netbox/templates/ipam/prefix/ip_ranges.html b/netbox/templates/ipam/prefix/ip_ranges.html index b262be821..268c290a1 100644 --- a/netbox/templates/ipam/prefix/ip_ranges.html +++ b/netbox/templates/ipam/prefix/ip_ranges.html @@ -10,9 +10,10 @@ {% endblock %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="IPRangeTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="IPRangeTable_config" %}
diff --git a/netbox/templates/ipam/prefix/prefixes.html b/netbox/templates/ipam/prefix/prefixes.html index 039b1ca3e..5d42596ba 100644 --- a/netbox/templates/ipam/prefix/prefixes.html +++ b/netbox/templates/ipam/prefix/prefixes.html @@ -12,9 +12,10 @@ {% endblock %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %}
diff --git a/netbox/templates/ipam/vlan/interfaces.html b/netbox/templates/ipam/vlan/interfaces.html index 51df17edc..5707d5364 100644 --- a/netbox/templates/ipam/vlan/interfaces.html +++ b/netbox/templates/ipam/vlan/interfaces.html @@ -2,9 +2,10 @@ {% load helpers %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="VLANDevicesTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="VLANDevicesTable_config" %}
{% include 'htmx/table.html' %} diff --git a/netbox/templates/ipam/vlan/vminterfaces.html b/netbox/templates/ipam/vlan/vminterfaces.html index f12e9df86..ef4a0730a 100644 --- a/netbox/templates/ipam/vlan/vminterfaces.html +++ b/netbox/templates/ipam/vlan/vminterfaces.html @@ -2,9 +2,10 @@ {% load helpers %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="VLANVirtualMachinesTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="VLANVirtualMachinesTable_config" %}
{% include 'htmx/table.html' %} diff --git a/netbox/templates/media_failure.html b/netbox/templates/media_failure.html index e3b7ef309..971b3cbc5 100644 --- a/netbox/templates/media_failure.html +++ b/netbox/templates/media_failure.html @@ -29,7 +29,7 @@
  • The HTTP service (e.g. nginx or Apache) is configured to serve files from the STATIC_ROOT path. - Refer to the installation + Refer to the installation documentation for further guidance.
      {% if request.user.is_staff or request.user.is_superuser %} diff --git a/netbox/templates/virtualization/cluster/devices.html b/netbox/templates/virtualization/cluster/devices.html index 075f34c7e..cb4a1b3ee 100644 --- a/netbox/templates/virtualization/cluster/devices.html +++ b/netbox/templates/virtualization/cluster/devices.html @@ -3,9 +3,10 @@ {% load render_table from django_tables2 %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="DeviceTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="DeviceTable_config" %}
      {% include 'htmx/table.html' %} diff --git a/netbox/templates/virtualization/cluster/virtual_machines.html b/netbox/templates/virtualization/cluster/virtual_machines.html index 8b4191259..953d9f940 100644 --- a/netbox/templates/virtualization/cluster/virtual_machines.html +++ b/netbox/templates/virtualization/cluster/virtual_machines.html @@ -3,9 +3,10 @@ {% load render_table from django_tables2 %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineTable_config" %}
      {% include 'htmx/table.html' %} diff --git a/netbox/templates/virtualization/virtualmachine/interfaces.html b/netbox/templates/virtualization/virtualmachine/interfaces.html index de657b3b3..e3ffb84d4 100644 --- a/netbox/templates/virtualization/virtualmachine/interfaces.html +++ b/netbox/templates/virtualization/virtualmachine/interfaces.html @@ -3,9 +3,10 @@ {% load helpers %} {% block content %} + {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineVMInterfaceTable_config" %} + {% csrf_token %} - {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineVMInterfaceTable_config" %}
      diff --git a/netbox/users/models.py b/netbox/users/models.py index 722ec5ba6..23068442e 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -173,11 +173,11 @@ class UserConfig(models.Model): @receiver(post_save, sender=User) -def create_userconfig(instance, created, **kwargs): +def create_userconfig(instance, created, raw=False, **kwargs): """ - Automatically create a new UserConfig when a new User is created. + Automatically create a new UserConfig when a new User is created. Skip this if importing a user from a fixture. """ - if created: + if created and not raw: config = get_config() UserConfig(user=instance, data=config.DEFAULT_USER_PREFERENCES).save() diff --git a/netbox/virtualization/tables/clusters.py b/netbox/virtualization/tables/clusters.py index 893d3c641..c9f87105d 100644 --- a/netbox/virtualization/tables/clusters.py +++ b/netbox/virtualization/tables/clusters.py @@ -14,7 +14,9 @@ class ClusterTypeTable(NetBoxTable): name = tables.Column( linkify=True ) - cluster_count = tables.Column( + cluster_count = columns.LinkedCountColumn( + viewname='virtualization:cluster_list', + url_params={'type_id': 'pk'}, verbose_name='Clusters' ) tags = columns.TagColumn( @@ -33,7 +35,9 @@ class ClusterGroupTable(NetBoxTable): name = tables.Column( linkify=True ) - cluster_count = tables.Column( + cluster_count = columns.LinkedCountColumn( + viewname='virtualization:cluster_list', + url_params={'group_id': 'pk'}, verbose_name='Clusters' ) contacts = tables.ManyToManyColumn( diff --git a/netbox/wireless/forms/models.py b/netbox/wireless/forms/models.py index 6d7dc84a9..d1012ba59 100644 --- a/netbox/wireless/forms/models.py +++ b/netbox/wireless/forms/models.py @@ -105,6 +105,9 @@ class WirelessLinkForm(NetBoxModelForm): ) location_a = DynamicModelChoiceField( queryset=Location.objects.all(), + query_params={ + 'site_id': '$site_a', + }, required=False, label='Location', initial_params={ @@ -142,6 +145,9 @@ class WirelessLinkForm(NetBoxModelForm): ) location_b = DynamicModelChoiceField( queryset=Location.objects.all(), + query_params={ + 'site_id': '$site_b', + }, required=False, label='Location', initial_params={ diff --git a/requirements.txt b/requirements.txt index 35867410b..32c13d455 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,11 +15,11 @@ djangorestframework==3.13.1 drf-yasg[validation]==1.20.0 graphene-django==2.15.0 gunicorn==20.1.0 -Jinja2==3.0.3 +Jinja2==3.1.2 Markdown==3.3.6 markdown-include==0.6.0 -mkdocs-material==8.2.9 -mkdocstrings==0.17.0 +mkdocs-material==8.2.11 +mkdocstrings[python-legacy]==0.18.1 netaddr==0.8.0 Pillow==9.1.0 psycopg2-binary==2.9.3 diff --git a/upgrade.sh b/upgrade.sh index 301225958..61e6106cd 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -3,23 +3,23 @@ # its most recent release. # This script will invoke Python with the value of the PYTHON environment -# variable (if set), or fall back to "python3". Note that NetBox v3.0+ requires -# Python 3.7 or later. +# variable (if set), or fall back to "python3". Note that NetBox v3.2+ requires +# Python 3.8 or later. cd "$(dirname "$0")" VIRTUALENV="$(pwd -P)/venv" PYTHON="${PYTHON:-python3}" # Validate the minimum required Python version -COMMAND="${PYTHON} -c 'import sys; exit(1 if sys.version_info < (3, 7) else 0)'" +COMMAND="${PYTHON} -c 'import sys; exit(1 if sys.version_info < (3, 8) else 0)'" PYTHON_VERSION=$(eval "${PYTHON} -V") eval $COMMAND || { echo "--------------------------------------------------------------------" echo "ERROR: Unsupported Python version: ${PYTHON_VERSION}. NetBox requires" - echo "Python 3.7 or later. To specify an alternate Python executable, set" + echo "Python 3.8 or later. To specify an alternate Python executable, set" echo "the PYTHON environment variable. For example:" echo "" - echo " sudo PYTHON=/usr/bin/python3.7 ./upgrade.sh" + echo " sudo PYTHON=/usr/bin/python3.8 ./upgrade.sh" echo "" echo "To show your current Python version: ${PYTHON} -V" echo "--------------------------------------------------------------------"