From 5d4ef5e9e54d81829f0b74e3f1529c285b8fa223 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 26 Apr 2023 13:27:55 -0700 Subject: [PATCH 01/55] 11607 make CableSerializer WritableNestedSerializer for to_internal value instantiation --- netbox/dcim/api/nested_serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/api/nested_serializers.py b/netbox/dcim/api/nested_serializers.py index 29881a548..f57451d17 100644 --- a/netbox/dcim/api/nested_serializers.py +++ b/netbox/dcim/api/nested_serializers.py @@ -425,7 +425,7 @@ class NestedInventoryItemRoleSerializer(WritableNestedSerializer): # Cables # -class NestedCableSerializer(BaseModelSerializer): +class NestedCableSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail') class Meta: From 4355085124204838235ba5ab71a203fae7b2fbbd Mon Sep 17 00:00:00 2001 From: x64x6a Date: Wed, 26 Apr 2023 13:02:32 -0700 Subject: [PATCH 02/55] Fixes #12355: Exclude additional characters from url encode --- netbox/extras/models/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 718cba5c1..b054882ea 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -279,7 +279,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogged text = clean_html(text, allowed_schemes) # Sanitize link - link = urllib.parse.quote_plus(link, safe='/:?&') + link = urllib.parse.quote_plus(link, safe='/:?&=%+[]@#') # Verify link scheme is allowed result = urllib.parse.urlparse(link) From b7140a0e4a7712fe75b4b1d6a47eeaa461c72e0a Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 27 Apr 2023 10:35:39 -0400 Subject: [PATCH 03/55] Closes #12343: Enforce a minimum length for SECRET_KEY configuration parameter --- docs/configuration/required-parameters.md | 6 ++---- docs/release-notes/version-3.5.md | 2 ++ netbox/netbox/settings.py | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/configuration/required-parameters.md b/docs/configuration/required-parameters.md index a71a1b410..c35f90f7b 100644 --- a/docs/configuration/required-parameters.md +++ b/docs/configuration/required-parameters.md @@ -144,8 +144,6 @@ REDIS = { ## SECRET_KEY -This is a secret, random string used to assist in the creation new cryptographic hashes for passwords and HTTP cookies. The key defined here should not be shared outside of the configuration file. `SECRET_KEY` can be changed at any time, however be aware that doing so will invalidate all existing sessions. +This is a secret, pseudorandom string used to assist in the creation new cryptographic hashes for passwords and HTTP cookies. The key defined here should not be shared outside the configuration file. `SECRET_KEY` can be changed at any time without impacting stored data, however be aware that doing so will invalidate all existing user sessions. NetBox deployments comprising multiple nodes must have the same secret key configured on all nodes. -Please note that this key is **not** used directly for hashing user passwords or for the encrypted storage of secret data in NetBox. - -`SECRET_KEY` should be at least 50 characters in length and contain a random mix of letters, digits, and symbols. The script located at `$INSTALL_ROOT/netbox/generate_secret_key.py` may be used to generate a suitable key. +`SECRET_KEY` **must** be at least 50 characters in length, and should contain a mix of letters, digits, and symbols. The script located at `$INSTALL_ROOT/netbox/generate_secret_key.py` may be used to generate a suitable key. Please note that this key is **not** used directly for hashing user passwords or for the encrypted storage of secret data in NetBox. diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 122ecb18b..bc54202b5 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -5,6 +5,7 @@ ### Breaking Changes * The `account` field has been removed from the provider model. This information is now tracked using the new provider account model. Multiple accounts can be assigned per provider. +* A minimum length of 50 characters is now enforced for the `SECRET_KEY` configuration parameter. * The JobResult model has been moved from the `extras` app to `core` and renamed to Job. Accordingly, its REST API endpoint has been moved from `/api/extras/job-results/` to `/api/core/jobs/`. * The `obj_type` field on the Job model (previously JobResult) has been renamed to `object_type` for consistency with other models. * The `JOBRESULT_RETENTION` configuration parameter has been renamed to `JOB_RETENTION`. @@ -72,6 +73,7 @@ Two new webhook trigger events have been introduced: `job_start` and `job_end`. * [#12068](https://github.com/netbox-community/netbox/issues/12068) - Enable generic foreign key relationships from jobs to NetBox objects * [#12085](https://github.com/netbox-community/netbox/issues/12085) - Add a file source view for reports * [#12218](https://github.com/netbox-community/netbox/issues/12218) - Provide more relevant API endpoint descriptions in schema +* [#12343](https://github.com/netbox-community/netbox/issues/12343) - Enforce a minimum length for `SECRET_KEY` configuration parameter ### Bug Fixes (From Beta2) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 8df61a7c9..83c9aed91 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -68,6 +68,15 @@ DATABASE = getattr(configuration, 'DATABASE') REDIS = getattr(configuration, 'REDIS') SECRET_KEY = getattr(configuration, 'SECRET_KEY') +# Enforce minimum length for SECRET_KEY +if type(SECRET_KEY) is not str: + raise ImproperlyConfigured(f"SECRET_KEY must be a string (found {type(SECRET_KEY).__name__})") +if len(SECRET_KEY) < 50: + raise ImproperlyConfigured( + f"SECRET_KEY must be at least 50 characters in length. To generate a suitable key, run the following command:\n" + f" python {BASE_DIR}/generate_secret_key.py" + ) + # Calculate a unique deployment ID from the secret key DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16] From 9a788349a94c925a21f3f4cd62d6fd79689db023 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 27 Apr 2023 08:26:14 -0700 Subject: [PATCH 04/55] 12252 allow sorting on object in search (#12357) * 12252 allow sorting on object in search * 12252 code review changes --- netbox/netbox/search/backends.py | 9 ++++++--- netbox/netbox/tables/tables.py | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/netbox/netbox/search/backends.py b/netbox/netbox/search/backends.py index f428842f5..4487b6bb8 100644 --- a/netbox/netbox/search/backends.py +++ b/netbox/netbox/search/backends.py @@ -145,9 +145,12 @@ class CachedValueSearchBackend(SearchBackend): ) # Omit any results pertaining to an object the user does not have permission to view - return [ - r for r in results if r.object is not None - ] + ret = [] + for r in results: + if r.object is not None: + r.name = str(r.object) + ret.append(r) + return ret def cache(self, instances, indexer=None, remove_existing=True): content_type = None diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index 3047719b7..ee171f1a8 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -204,7 +204,8 @@ class SearchTable(tables.Table): order_by="object___meta__verbose_name", ) object = tables.Column( - linkify=True + linkify=True, + order_by=('name', ) ) field = tables.Column() value = tables.Column() From 84089ab8c5e1b5107c7ae75bb4cad53d900ef9d2 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 27 Apr 2023 11:47:42 -0400 Subject: [PATCH 05/55] Release v3.4.10 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/version-3.4.md | 10 ++++++++++ netbox/netbox/settings.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 956b682b0..c71ee6f3e 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.4.9 + placeholder: v3.4.10 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index d3f337175..16e4981cb 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.4.9 + placeholder: v3.4.10 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-3.4.md b/docs/release-notes/version-3.4.md index bcb3a9ad2..22c33bb01 100644 --- a/docs/release-notes/version-3.4.md +++ b/docs/release-notes/version-3.4.md @@ -1,5 +1,15 @@ # NetBox v3.4 +## v3.4.10 (2023-04-27) + +### Bug Fixes + +* [#11607](https://github.com/netbox-community/netbox/issues/11607) - Fix custom object field assignments made via REST API for for cables +* [#12252](https://github.com/netbox-community/netbox/issues/12252) - Fix ordering of search results when sorting by object name +* [#12355](https://github.com/netbox-community/netbox/issues/12355) - Fix escaping of certain characters in URL when rendering custom links + +--- + ## v3.4.9 (2023-04-26) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 03970ff75..82c9c7af2 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW # Environment setup # -VERSION = '3.4.9' +VERSION = '3.4.10' # Hostname HOSTNAME = platform.node() From b3efb14176290edfc73c0f48fb3aaa5fc55973ee Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 27 Apr 2023 14:04:30 -0400 Subject: [PATCH 06/55] Remove pinned version dependencies --- base_requirements.txt | 5 ++--- netbox/utilities/utils.py | 4 ++-- requirements.txt | 5 +---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/base_requirements.txt b/base_requirements.txt index c1e2fed7f..1e9a45048 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -1,6 +1,6 @@ # HTML sanitizer # https://github.com/mozilla/bleach/blob/main/CHANGES -bleach<6.0 +bleach # Python client for Amazon AWS API # https://github.com/boto/boto3/blob/develop/CHANGELOG.rst @@ -137,8 +137,7 @@ social-auth-core # Django app for social-auth-core # https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md -# See https://github.com/python-social-auth/social-app-django/issues/429 -social-auth-app-django==5.0.0 +social-auth-app-django # SVG image rendering (used for rack elevations) # hhttps://github.com/mozman/svgwrite/blob/master/NEWS.rst diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index 57092bb7d..b1504e62f 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -491,14 +491,14 @@ def clean_html(html, schemes): Also takes a list of allowed URI schemes. """ - ALLOWED_TAGS = [ + ALLOWED_TAGS = { "div", "pre", "code", "blockquote", "del", "hr", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "p", "br", "strong", "em", "a", "b", "i", "img", "table", "thead", "tbody", "tr", "th", "td", "dl", "dt", "dd", - ] + } ALLOWED_ATTRIBUTES = { "div": ['class'], diff --git a/requirements.txt b/requirements.txt index 024905a35..191399bd3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -bleach==5.0.1 +bleach==6.0.0 boto3==1.26.115 Django==4.1.8 django-cors-headers==3.14.0 @@ -35,6 +35,3 @@ social-auth-core[openidconnect]==4.4.2 svgwrite==1.4.3 tablib==3.4.0 tzdata==2023.3 - -# Workaround for #7401 -jsonschema==3.2.0 From a5bc9d4a2de65afaf9655f57b0faa72b415811dc Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 27 Apr 2023 14:24:44 -0400 Subject: [PATCH 07/55] Release v3.5.0 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/index.md | 10 ++++++++++ docs/release-notes/version-3.5.md | 4 ++-- netbox/netbox/settings.py | 2 +- requirements.txt | 4 ++-- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index b4240171b..672f43b09 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.5-beta2 + placeholder: v3.5.0 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index cedee1f44..842454d6b 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.5-beta2 + placeholder: v3.5.0 validations: required: true - type: dropdown diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index 6262ef92c..4d812938f 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -10,6 +10,16 @@ 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. +#### [Version 3.5](./version-3.5.md) (April 2023) + +* Customizable Dashboard ([#9416](https://github.com/netbox-community/netbox/issues/9416)) +* Remote Data Sources ([#11558](https://github.com/netbox-community/netbox/issues/11558)) +* Configuration Template Rendering ([#11559](https://github.com/netbox-community/netbox/issues/11559)) +* NAPALM Integration Plugin ([#10520](https://github.com/netbox-community/netbox/issues/10520)) +* ASN Ranges ([#8550](https://github.com/netbox-community/netbox/issues/8550)) +* Provider Accounts ([#9047](https://github.com/netbox-community/netbox/issues/9047)) +* Job-Triggered Webhooks ([#8958](https://github.com/netbox-community/netbox/issues/8958)) + #### [Version 3.4](./version-3.4.md) (December 2022) * New Global Search ([#10560](https://github.com/netbox-community/netbox/issues/10560)) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index bc54202b5..13fee4473 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -1,6 +1,6 @@ # NetBox v3.5 -## v3.5-beta2 (2023-04-18) +## v3.5.0 (2023-04-27) ### Breaking Changes @@ -29,7 +29,7 @@ NetBox now has the ability to synchronize arbitrary data from external sources t This release introduces the ability to render device configurations from Jinja2 templates natively within NetBox, via both the UI and REST API. The new [ConfigTemplate](../models/extras/configtemplate.md) model stores template code (which may be defined locally or sourced from remote data files). The rendering engine passes data gleaned from both config contexts and request parameters to generate complete configurations suitable for direct application to network devices. -#### NAPALM Plugin ([#10520](https://github.com/netbox-community/netbox/issues/10520)) +#### NAPALM Integration Plugin ([#10520](https://github.com/netbox-community/netbox/issues/10520)) The NAPALM integration feature found in previous NetBox releases has been moved from the core application to a [dedicated plugin](https://github.com/netbox-community/netbox-napalm). This allows greater control over the feature's configuration and will unlock additional potential as a separate project. diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 83c9aed91..c35ebe992 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW # Environment setup # -VERSION = '3.5-beta2' +VERSION = '3.5.0' # Hostname HOSTNAME = platform.node() diff --git a/requirements.txt b/requirements.txt index 191399bd3..3ac6d636f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ bleach==6.0.0 -boto3==1.26.115 +boto3==1.26.121 Django==4.1.8 django-cors-headers==3.14.0 django-debug-toolbar==4.0.0 @@ -30,7 +30,7 @@ Pillow==9.5.0 psycopg2-binary==2.9.6 PyYAML==6.0 sentry-sdk==1.21.0 -social-auth-app-django==5.0.0 +social-auth-app-django==5.2.0 social-auth-core[openidconnect]==4.4.2 svgwrite==1.4.3 tablib==3.4.0 From 25dc7e234dcb11ee230fed82356c8716a36538f1 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 27 Apr 2023 14:52:07 -0400 Subject: [PATCH 08/55] PRVB --- docs/release-notes/version-3.5.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 13fee4473..98735c9bf 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -1,5 +1,9 @@ # NetBox v3.5 +## v3.5.1 (FUTURE) + +--- + ## v3.5.0 (2023-04-27) ### Breaking Changes diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index c35ebe992..01a1e2090 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -25,7 +25,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW # Environment setup # -VERSION = '3.5.0' +VERSION = '3.5.1-dev' # Hostname HOSTNAME = platform.node() From aabaeec1d761fec501a0a66b62dc479dd4abe424 Mon Sep 17 00:00:00 2001 From: kkthxbye-code Date: Fri, 28 Apr 2023 12:17:39 +0200 Subject: [PATCH 09/55] Add ObjectChange as a valid option for ObjectListWidget and ObjectCountsWidget --- netbox/extras/dashboard/widgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py index 95460eb75..0018cf547 100644 --- a/netbox/extras/dashboard/widgets.py +++ b/netbox/extras/dashboard/widgets.py @@ -8,6 +8,7 @@ from django import forms from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.core.cache import cache +from django.db.models import Q from django.template.loader import render_to_string from django.urls import NoReverseMatch, reverse from django.utils.translation import gettext as _ @@ -33,7 +34,7 @@ def get_content_type_labels(): return [ (content_type_identifier(ct), content_type_name(ct)) for ct in ContentType.objects.filter( - FeatureQuery('export_templates').get_query() + FeatureQuery('export_templates').get_query() | Q(app_label='extras', model='objectchange') ).order_by('app_label', 'model') ] From 6206d226aea610a49ebf6f3898cd3422a825378c Mon Sep 17 00:00:00 2001 From: kkthxbye-code Date: Mon, 1 May 2023 07:15:00 +0200 Subject: [PATCH 10/55] Fix the filtervar being set to site_id instead of site in SiteView related_models --- netbox/dcim/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index ee97900a4..bcbbf1739 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -371,7 +371,7 @@ class SiteView(generic.ObjectView): (VLANGroup.objects.restrict(request.user, 'view').filter( scope_type=ContentType.objects.get_for_model(Site), scope_id=instance.pk - ), 'site_id'), + ), 'site'), (VLAN.objects.restrict(request.user, 'view').filter(site=instance), 'site_id'), # Circuits (Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).distinct(), 'site_id'), From 528fb21a7eb066d8a2dec6285d57b51cba1c3fe5 Mon Sep 17 00:00:00 2001 From: kkthxbye-code Date: Mon, 1 May 2023 07:44:15 +0200 Subject: [PATCH 11/55] Mark provider_account as not required in API and bulk import --- netbox/circuits/api/serializers.py | 2 +- netbox/circuits/forms/bulk_import.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py index 5635b6730..f4abda645 100644 --- a/netbox/circuits/api/serializers.py +++ b/netbox/circuits/api/serializers.py @@ -106,7 +106,7 @@ class CircuitCircuitTerminationSerializer(WritableNestedSerializer): class CircuitSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail') provider = NestedProviderSerializer() - provider_account = NestedProviderAccountSerializer() + provider_account = NestedProviderAccountSerializer(required=False, allow_null=True) status = ChoiceField(choices=CircuitStatusChoices, required=False) type = NestedCircuitTypeSerializer() tenant = NestedTenantSerializer(required=False, allow_null=True) diff --git a/netbox/circuits/forms/bulk_import.py b/netbox/circuits/forms/bulk_import.py index d55831008..3941ef574 100644 --- a/netbox/circuits/forms/bulk_import.py +++ b/netbox/circuits/forms/bulk_import.py @@ -74,7 +74,8 @@ class CircuitImportForm(NetBoxModelImportForm): provider_account = CSVModelChoiceField( queryset=ProviderAccount.objects.all(), to_field_name='name', - help_text=_('Assigned provider account') + help_text=_('Assigned provider account'), + required=False ) type = CSVModelChoiceField( queryset=CircuitType.objects.all(), From 47f302340137f2cb8fc231bccc2da8c792c79b51 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 13:06:10 -0400 Subject: [PATCH 12/55] Fixes #12412: Device/VM interface MAC addresses can be nullified via REST API --- netbox/dcim/api/serializers.py | 6 +++++- netbox/virtualization/api/serializers.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 8d620c408..3f6d55da7 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -904,7 +904,11 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect ) count_ipaddresses = serializers.IntegerField(read_only=True) count_fhrp_groups = serializers.IntegerField(read_only=True) - mac_address = serializers.CharField(required=False, default=None) + mac_address = serializers.CharField( + required=False, + default=None, + allow_null=True + ) wwn = serializers.CharField(required=False, default=None) class Meta: diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index 7d0f1107e..f72215b98 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -126,7 +126,11 @@ class VMInterfaceSerializer(NetBoxModelSerializer): l2vpn_termination = NestedL2VPNTerminationSerializer(read_only=True, allow_null=True) count_ipaddresses = serializers.IntegerField(read_only=True) count_fhrp_groups = serializers.IntegerField(read_only=True) - mac_address = serializers.CharField(required=False, default=None) + mac_address = serializers.CharField( + required=False, + default=None, + allow_null=True + ) class Meta: model = VMInterface From 139ef7ef4cb4e22f9499c864ab90c0b3245db15b Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 14:17:50 -0400 Subject: [PATCH 13/55] Fixes #12395: Fix "create & add another" action for objects with custom fields --- netbox/netbox/api/serializers/features.py | 24 +---------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/netbox/netbox/api/serializers/features.py b/netbox/netbox/api/serializers/features.py index 5332a22d6..1374ba526 100644 --- a/netbox/netbox/api/serializers/features.py +++ b/netbox/netbox/api/serializers/features.py @@ -14,35 +14,13 @@ __all__ = ( class CustomFieldModelSerializer(serializers.Serializer): """ - Introduces support for custom field assignment. Adds `custom_fields` serialization and ensures - that custom field data is populated upon initialization. + Introduces support for custom field assignment and representation. """ custom_fields = CustomFieldsDataField( source='custom_field_data', default=CreateOnlyDefault(CustomFieldDefaultValues()) ) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if self.instance is not None: - - # Retrieve the set of CustomFields which apply to this type of object - content_type = ContentType.objects.get_for_model(self.Meta.model) - fields = CustomField.objects.filter(content_types=content_type) - - # Populate custom field values for each instance from database - if type(self.instance) in (list, tuple): - for obj in self.instance: - self._populate_custom_fields(obj, fields) - else: - self._populate_custom_fields(self.instance, fields) - - def _populate_custom_fields(self, instance, custom_fields): - instance.custom_fields = {} - for field in custom_fields: - instance.custom_fields[field.name] = instance.cf.get(field.name) - class TaggableModelSerializer(serializers.Serializer): """ From 8cede0daf8a6af6992a6e09ed3f4896d6efeb65d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 14:19:56 -0400 Subject: [PATCH 14/55] Changelog for #12380, #12395, #12396, #12405, #12412 --- docs/release-notes/version-3.5.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 98735c9bf..45dff3a41 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -2,6 +2,14 @@ ## v3.5.1 (FUTURE) +### Bug Fixes + +* [#12380](https://github.com/netbox-community/netbox/issues/12380) - Allow selecting object change as model under object list widget configuration +* [#12395](https://github.com/netbox-community/netbox/issues/12395) - Fix "create & add another" action for objects with custom fields +* [#12396](https://github.com/netbox-community/netbox/issues/12396) - Provider account should not be a required field in REST API serializer +* [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view +* [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API + --- ## v3.5.0 (2023-04-27) From 261f5e4995b5894561bca3503e1876b24bfd4c40 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 14:38:51 -0400 Subject: [PATCH 15/55] Fixes #12415: Pin rq to v1.13.0 to fix ImportError exception from django-rq --- docs/release-notes/version-3.5.md | 1 + requirements.txt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 45dff3a41..a22728b53 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -9,6 +9,7 @@ * [#12396](https://github.com/netbox-community/netbox/issues/12396) - Provider account should not be a required field in REST API serializer * [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view * [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API +* [#12415](https://github.com/netbox-community/netbox/issues/12415) - Pin `rq` to v1.13.0 to fix `ImportError` exception from `django-rq` --- diff --git a/requirements.txt b/requirements.txt index 3ac6d636f..9bf7144cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,3 +35,6 @@ social-auth-core[openidconnect]==4.4.2 svgwrite==1.4.3 tablib==3.4.0 tzdata==2023.3 + +# Pin rq to v1.13.0 to retain compatibility with django-rq +rq==1.13.0 From 9319cffb1c8b17f4750e6a77e95de3113026c68f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 16:19:39 -0400 Subject: [PATCH 16/55] Fixes #12384: Add a three-second timeout for RSS reader widget --- docs/release-notes/version-3.5.md | 1 + netbox/extras/dashboard/widgets.py | 50 ++++++++++++------- .../extras/dashboard/widgets/rssfeed.html | 10 ++-- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index a22728b53..254cc7391 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -5,6 +5,7 @@ ### Bug Fixes * [#12380](https://github.com/netbox-community/netbox/issues/12380) - Allow selecting object change as model under object list widget configuration +* [#12384](https://github.com/netbox-community/netbox/issues/12384) - Add a three-second timeout for RSS reader widget * [#12395](https://github.com/netbox-community/netbox/issues/12395) - Fix "create & add another" action for objects with custom fields * [#12396](https://github.com/netbox-community/netbox/issues/12396) - Provider account should not be a required field in REST API serializer * [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py index 0018cf547..9cd53763a 100644 --- a/netbox/extras/dashboard/widgets.py +++ b/netbox/extras/dashboard/widgets.py @@ -4,6 +4,7 @@ from hashlib import sha256 from urllib.parse import urlencode import feedparser +import requests from django import forms from django.conf import settings from django.contrib.contenttypes.models import ContentType @@ -269,12 +270,9 @@ class RSSFeedWidget(DashboardWidget): ) def render(self, request): - url = self.config['feed_url'] - feed = self.get_feed() - return render_to_string(self.template_name, { - 'url': url, - 'feed': feed, + 'url': self.config['feed_url'], + **self.get_feed() }) @cached_property @@ -286,17 +284,33 @@ class RSSFeedWidget(DashboardWidget): def get_feed(self): # Fetch RSS content from cache if available if feed_content := cache.get(self.cache_key): - feed = feedparser.FeedParserDict(feed_content) - else: - feed = feedparser.parse( - self.config['feed_url'], - request_headers={'User-Agent': f'NetBox/{settings.VERSION}'} - ) - if not feed.bozo: - # Cap number of entries - max_entries = self.config.get('max_entries') - feed['entries'] = feed['entries'][:max_entries] - # Cache the feed content - cache.set(self.cache_key, dict(feed), self.config.get('cache_timeout')) + return { + 'feed': feedparser.FeedParserDict(feed_content), + } - return feed + # Fetch feed content from remote server + try: + response = requests.get( + url=self.config['feed_url'], + headers={'User-Agent': f'NetBox/{settings.VERSION}'}, + proxies=settings.HTTP_PROXIES, + timeout=3 + ) + response.raise_for_status() + except requests.exceptions.RequestException as e: + return { + 'error': e, + } + + # Parse feed content + feed = feedparser.parse(response.content) + if not feed.bozo: + # Cap number of entries + max_entries = self.config.get('max_entries') + feed['entries'] = feed['entries'][:max_entries] + # Cache the feed content + cache.set(self.cache_key, dict(feed), self.config.get('cache_timeout')) + + return { + 'feed': feed, + } diff --git a/netbox/templates/extras/dashboard/widgets/rssfeed.html b/netbox/templates/extras/dashboard/widgets/rssfeed.html index 5de3c3105..c304b7c07 100644 --- a/netbox/templates/extras/dashboard/widgets/rssfeed.html +++ b/netbox/templates/extras/dashboard/widgets/rssfeed.html @@ -1,4 +1,4 @@ -{% if not feed.bozo %} +{% if feed and not feed.bozo %}
{% for entry in feed.entries %}
@@ -16,7 +16,9 @@ There was a problem fetching the RSS feed: -
-Response status: {{ feed.status }}
-Error: {{ feed.bozo_exception|escape }}
+ {% if feed %} + {{ feed.bozo_exception|escape }} (HTTP {{ feed.status }}) + {% else %} + {{ error }} + {% endif %} {% endif %} From 19527077028cb4dfe31bf7494820c70294c11f2e Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 16:51:10 -0400 Subject: [PATCH 17/55] Fixes #12401: Support the creation of front ports without a pre-populated device ID --- docs/release-notes/version-3.5.md | 1 + netbox/dcim/forms/object_create.py | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 254cc7391..90340ac19 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -8,6 +8,7 @@ * [#12384](https://github.com/netbox-community/netbox/issues/12384) - Add a three-second timeout for RSS reader widget * [#12395](https://github.com/netbox-community/netbox/issues/12395) - Fix "create & add another" action for objects with custom fields * [#12396](https://github.com/netbox-community/netbox/issues/12396) - Provider account should not be a required field in REST API serializer +* [#12401](https://github.com/netbox-community/netbox/issues/12401) - Support the creation of front ports without a pre-populated device ID * [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view * [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API * [#12415](https://github.com/netbox-community/netbox/issues/12415) - Pin `rq` to v1.13.0 to fix `ImportError` exception from `django-rq` diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index 3507faf3b..236077421 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext as _ from dcim.models import * from netbox.forms import NetBoxModelForm from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField +from utilities.forms.widgets import APISelect from . import model_forms __all__ = ( @@ -225,6 +226,18 @@ class InterfaceCreateForm(ComponentCreateForm, model_forms.InterfaceForm): class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all(), + selector=True, + widget=APISelect( + # TODO: Clean up the application of HTMXSelect attributes + attrs={ + 'hx-get': '.', + 'hx-include': f'#form_fields', + 'hx-target': f'#form_fields', + } + ) + ) rear_port = forms.MultipleChoiceField( choices=[], label=_('Rear ports'), @@ -244,9 +257,10 @@ class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - device = Device.objects.get( - pk=self.initial.get('device') or self.data.get('device') - ) + if device_id := self.data.get('device') or self.initial.get('device'): + device = Device.objects.get(pk=device_id) + else: + return # Determine which rear port positions are occupied. These will be excluded from the list of available # mappings. From 20f046482460e649a68b3d6347d5aba4a2500adc Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Mon, 1 May 2023 10:45:19 -0400 Subject: [PATCH 18/55] Fix OpanAPI schema base path --- netbox/core/api/schema.py | 15 ++------------- netbox/netbox/settings.py | 8 +++++--- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/netbox/core/api/schema.py b/netbox/core/api/schema.py index d06d1d3bf..9550df3ea 100644 --- a/netbox/core/api/schema.py +++ b/netbox/core/api/schema.py @@ -1,23 +1,12 @@ import re import typing -from drf_spectacular.extensions import ( - OpenApiSerializerFieldExtension, - OpenApiViewExtension, -) +from drf_spectacular.extensions import OpenApiSerializerFieldExtension from drf_spectacular.openapi import AutoSchema from drf_spectacular.plumbing import ( - ComponentRegistry, - ResolvedComponent, - build_basic_type, - build_choice_field, - build_media_type_object, - build_object_type, - get_doc, - is_serializer, + build_basic_type, build_choice_field, build_media_type_object, build_object_type, get_doc, ) from drf_spectacular.types import OpenApiTypes -from drf_spectacular.utils import extend_schema from rest_framework.relations import ManyRelatedField from netbox.api.fields import ChoiceField, SerializedPKRelatedField diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 01a1e2090..6608fcb8f 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -616,13 +616,15 @@ REST_FRAMEWORK = { # SPECTACULAR_SETTINGS = { - 'TITLE': 'NetBox API', - 'DESCRIPTION': 'API to access NetBox', + 'TITLE': 'NetBox REST API', 'LICENSE': {'name': 'Apache v2 License'}, 'VERSION': VERSION, 'COMPONENT_SPLIT_REQUEST': True, 'REDOC_DIST': 'SIDECAR', - 'SERVERS': [{'url': f'/{BASE_PATH}api'}], + 'SERVERS': [{ + 'url': BASE_PATH, + 'description': 'NetBox', + }], 'SWAGGER_UI_DIST': 'SIDECAR', 'SWAGGER_UI_FAVICON_HREF': 'SIDECAR', 'POSTPROCESSING_HOOKS': [], From 3bdbf67b8f18eaf52ca45ed9d97db1dbed56b6d1 Mon Sep 17 00:00:00 2001 From: Jop Zinkweg Date: Tue, 2 May 2023 10:21:09 +0200 Subject: [PATCH 19/55] Update examples to reflect obj rename to object #8684 missed some references to ```obj``` --- docs/customization/custom-links.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/customization/custom-links.md b/docs/customization/custom-links.md index 5d1cd4556..baae1db4f 100644 --- a/docs/customization/custom-links.md +++ b/docs/customization/custom-links.md @@ -2,12 +2,12 @@ Custom links allow users to display arbitrary hyperlinks to external content within NetBox object views. These are helpful for cross-referencing related records in systems outside NetBox. For example, you might create a custom link on the device view which links to the current device in a Network Monitoring System (NMS). -Custom links are created by navigating to Customization > Custom Links. Each link is associated with a particular NetBox object type (site, device, prefix, etc.) and will be displayed on relevant views. Each link has display text and a URL, and data from the NetBox item being viewed can be included in the link using [Jinja2 template code](https://jinja2docs.readthedocs.io/en/stable/) through the variable `obj`, and custom fields through `obj.cf`. +Custom links are created by navigating to Customization > Custom Links. Each link is associated with a particular NetBox object type (site, device, prefix, etc.) and will be displayed on relevant views. Each link has display text and a URL, and data from the NetBox item being viewed can be included in the link using [Jinja2 template code](https://jinja2docs.readthedocs.io/en/stable/) through the variable `object`, and custom fields through `object.cf`. For example, you might define a link like this: * Text: `View NMS` -* URL: `https://nms.example.com/nodes/?name={{ obj.name }}` +* URL: `https://nms.example.com/nodes/?name={{ object.name }}` When viewing a device named Router4, this link would render as: @@ -43,7 +43,7 @@ Only links which render with non-empty text are included on the page. You can em For example, if you only want to display a link for active devices, you could set the link text to ```jinja2 -{% if obj.status == 'active' %}View NMS{% endif %} +{% if object.status == 'active' %}View NMS{% endif %} ``` The link will not appear when viewing a device with any status other than "active." @@ -51,7 +51,7 @@ The link will not appear when viewing a device with any status other than "activ As another example, if you wanted to show only devices belonging to a certain manufacturer, you could do something like this: ```jinja2 -{% if obj.device_type.manufacturer.name == 'Cisco' %}View NMS{% endif %} +{% if object.device_type.manufacturer.name == 'Cisco' %}View NMS{% endif %} ``` The link will only appear when viewing a device with a manufacturer name of "Cisco." From 2099cd0fdc662a7c9446d7fc81e716540e847fd2 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 2 May 2023 08:46:54 -0400 Subject: [PATCH 20/55] #12415: Bump django-rq to v2.8.0 --- docs/release-notes/version-3.5.md | 2 +- requirements.txt | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 90340ac19..f0ad05a9a 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -11,7 +11,7 @@ * [#12401](https://github.com/netbox-community/netbox/issues/12401) - Support the creation of front ports without a pre-populated device ID * [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view * [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API -* [#12415](https://github.com/netbox-community/netbox/issues/12415) - Pin `rq` to v1.13.0 to fix `ImportError` exception from `django-rq` +* [#12415](https://github.com/netbox-community/netbox/issues/12415) - Fix `ImportError` exception when running RQ worker --- diff --git a/requirements.txt b/requirements.txt index 9bf7144cf..bdcb9a091 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ django-pglocks==1.0.4 django-prometheus==2.2.0 django-redis==5.2.0 django-rich==1.5.0 -django-rq==2.7.0 +django-rq==2.8.0 django-tables2==2.5.3 django-taggit==3.1.0 django-timezone-field==5.0 @@ -35,6 +35,3 @@ social-auth-core[openidconnect]==4.4.2 svgwrite==1.4.3 tablib==3.4.0 tzdata==2023.3 - -# Pin rq to v1.13.0 to retain compatibility with django-rq -rq==1.13.0 From cc3b95bdb072883d417632dfee8026951f769729 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 2 May 2023 09:28:15 -0400 Subject: [PATCH 21/55] Fixes #12400: Validate default values for object and multi-object custom fields --- docs/release-notes/version-3.5.md | 1 + netbox/extras/models/customfields.py | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index f0ad05a9a..d6e7bdb53 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -8,6 +8,7 @@ * [#12384](https://github.com/netbox-community/netbox/issues/12384) - Add a three-second timeout for RSS reader widget * [#12395](https://github.com/netbox-community/netbox/issues/12395) - Fix "create & add another" action for objects with custom fields * [#12396](https://github.com/netbox-community/netbox/issues/12396) - Provider account should not be a required field in REST API serializer +* [#12400](https://github.com/netbox-community/netbox/issues/12400) - Validate default values for object and multi-object custom fields * [#12401](https://github.com/netbox-community/netbox/issues/12401) - Support the creation of front ports without a pre-populated device ID * [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view * [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 18430300f..439d15edc 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -606,5 +606,18 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): f"Invalid choice(s) ({', '.join(value)}). Available choices are: {', '.join(self.choices)}" ) + # Validate selected object + elif self.type == CustomFieldTypeChoices.TYPE_OBJECT: + if type(value) is not int: + raise ValidationError(f"Value must be an object ID, not {type(value).__name__}") + + # Validate selected objects + elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT: + if type(value) is not list: + raise ValidationError(f"Value must be a list of object IDs, not {type(value).__name__}") + for id in value: + if type(id) is not int: + raise ValidationError(f"Found invalid object ID: {id}") + elif self.required: raise ValidationError("Required field cannot be empty.") From a566a56a64f5ab00f0d3787df87187bce1755881 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 27 Apr 2023 22:24:09 +0530 Subject: [PATCH 22/55] fixes RelatedObjectDoesNotExist #12367 --- netbox/netbox/models/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index fe25bb837..c0f679e4f 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -67,8 +67,8 @@ class NetBoxModel(CloningMixin, NetBoxFeatureSet, models.Model): for field in self._meta.get_fields(): if isinstance(field, GenericForeignKey): - ct_value = getattr(self, field.ct_field) - fk_value = getattr(self, field.fk_field) + ct_value = getattr(self, field.ct_field, None) + fk_value = getattr(self, field.fk_field, None) if ct_value is None and fk_value is not None: raise ValidationError({ From fbcf4c268bf7751f761f36834e87c7354570ad0f Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 2 May 2023 10:59:55 -0700 Subject: [PATCH 23/55] 12436 remove add button from contactassignment list view --- netbox/tenancy/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index ba7249c8d..b9ada8640 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -352,6 +352,7 @@ class ContactAssignmentListView(generic.ObjectListView): filterset = filtersets.ContactAssignmentFilterSet filterset_form = forms.ContactAssignmentFilterForm table = tables.ContactAssignmentTable + actions = ('export', 'bulk_edit', 'bulk_delete') @register_model_view(ContactAssignment, 'edit') From 7aa8434575f0c69f0c314c5316386ddb8b445b49 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 2 May 2023 16:40:56 -0400 Subject: [PATCH 24/55] Changelog for #12367, #12410, #12436 --- docs/release-notes/version-3.5.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index d6e7bdb53..168f15fcb 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -4,6 +4,7 @@ ### Bug Fixes +* [#12367](https://github.com/netbox-community/netbox/issues/12367) - Fix `RelatedObjectDoesNotExist` exception under certain conditions (regression from #11550) * [#12380](https://github.com/netbox-community/netbox/issues/12380) - Allow selecting object change as model under object list widget configuration * [#12384](https://github.com/netbox-community/netbox/issues/12384) - Add a three-second timeout for RSS reader widget * [#12395](https://github.com/netbox-community/netbox/issues/12395) - Fix "create & add another" action for objects with custom fields @@ -11,8 +12,10 @@ * [#12400](https://github.com/netbox-community/netbox/issues/12400) - Validate default values for object and multi-object custom fields * [#12401](https://github.com/netbox-community/netbox/issues/12401) - Support the creation of front ports without a pre-populated device ID * [#12405](https://github.com/netbox-community/netbox/issues/12405) - Fix filtering for VLAN groups displayed under site view +* [#12410](https://github.com/netbox-community/netbox/issues/12410) - Fix base path for OpenAPI schema (fixes Swagger UI requests) * [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API * [#12415](https://github.com/netbox-community/netbox/issues/12415) - Fix `ImportError` exception when running RQ worker +* [#12436](https://github.com/netbox-community/netbox/issues/12436) - Remove extraneous "add" button from contact assignments list --- From b14a514b4752bd69dd1b6495ff8f1d5678aab95c Mon Sep 17 00:00:00 2001 From: Austin de Coup-Crank <94914780+decoupca@users.noreply.github.com> Date: Tue, 2 May 2023 15:47:22 -0500 Subject: [PATCH 25/55] Closes 10759: Enable markdown support for custom field descriptions (#12344) * enable markdown in custom field descriptions * Closes #10759: Enable markdown support for custom field descriptions --------- Co-authored-by: Jeremy Stretch --- netbox/extras/tables/tables.py | 1 + netbox/templates/extras/customfield.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 6787b0c75..59b45c059 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -29,6 +29,7 @@ class CustomFieldTable(NetBoxTable): content_types = columns.ContentTypesColumn() required = columns.BooleanColumn() ui_visibility = columns.ChoiceFieldColumn(verbose_name="UI visibility") + description = columns.MarkdownColumn() is_cloneable = columns.BooleanColumn() class Meta(NetBoxTable.Meta): diff --git a/netbox/templates/extras/customfield.html b/netbox/templates/extras/customfield.html index 4c103d4c6..b783c8a77 100644 --- a/netbox/templates/extras/customfield.html +++ b/netbox/templates/extras/customfield.html @@ -32,7 +32,7 @@ Description - {{ object.description|placeholder }} + {{ object.description|markdown|placeholder }} Required From 24650d911813e07424eb9f414d00a4a4ab24f377 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 2 May 2023 16:25:41 -0700 Subject: [PATCH 26/55] 11422 add power-panel-name to quick search of power feeds --- netbox/dcim/filtersets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 207fb6d00..fccaa72f0 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1900,6 +1900,7 @@ class PowerFeedFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet, PathEndpoi return queryset qs_filter = ( Q(name__icontains=value) | + Q(power_panel__name__icontains=value) | Q(comments__icontains=value) ) return queryset.filter(qs_filter) From 78ec3a6411c57b29b7da3d885845c78f9b3062da Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 2 May 2023 16:11:58 -0700 Subject: [PATCH 27/55] 11504 add rack filter to elevation --- netbox/dcim/forms/filtersets.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index a00c7fe26..d31bba030 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -298,6 +298,15 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilte class RackElevationFilterForm(RackFilterForm): + fieldsets = ( + (None, ('q', 'filter_id', 'tag')), + ('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'id')), + ('Function', ('status', 'role_id')), + ('Hardware', ('type', 'width', 'serial', 'asset_tag')), + ('Tenant', ('tenant_group_id', 'tenant_id')), + ('Contacts', ('contact', 'contact_role', 'contact_group')), + ('Weight', ('weight', 'max_weight', 'weight_unit')), + ) id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), label=_('Rack'), From 5036020dc0146f928c8e34e3faf3a8746e62fd8e Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 3 May 2023 06:20:53 -0700 Subject: [PATCH 28/55] 12433 update object list widget to correctly parameterize urls (#12434) * 12433 update object list widget to correctly parameterize urls * Update netbox/extras/dashboard/widgets.py Co-authored-by: Jeremy Stretch * 12433 fix indent * Correct per_page query parameter --------- Co-authored-by: Jeremy Stretch --- netbox/extras/dashboard/widgets.py | 7 +++++-- netbox/templates/extras/dashboard/widgets/objectlist.html | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py index 9cd53763a..69d1cc36d 100644 --- a/netbox/extras/dashboard/widgets.py +++ b/netbox/extras/dashboard/widgets.py @@ -229,7 +229,11 @@ class ObjectListWidget(DashboardWidget): htmx_url = reverse(viewname) except NoReverseMatch: htmx_url = None - if parameters := self.config.get('url_params'): + parameters = self.config.get('url_params') or {} + if page_size := self.config.get('page_size'): + parameters['per_page'] = page_size + + if parameters: try: htmx_url = f'{htmx_url}?{urlencode(parameters, doseq=True)}' except ValueError: @@ -238,7 +242,6 @@ class ObjectListWidget(DashboardWidget): 'viewname': viewname, 'has_permission': has_permission, 'htmx_url': htmx_url, - 'page_size': self.config.get('page_size'), }) diff --git a/netbox/templates/extras/dashboard/widgets/objectlist.html b/netbox/templates/extras/dashboard/widgets/objectlist.html index 76c4e658c..54f8094b3 100644 --- a/netbox/templates/extras/dashboard/widgets/objectlist.html +++ b/netbox/templates/extras/dashboard/widgets/objectlist.html @@ -1,5 +1,5 @@ {% if htmx_url and has_permission %} -
+
{% elif htmx_url %}
No permission to view this content. From 01fa6e28cd7bf61c5469efea7310ccb14a767df9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 3 May 2023 09:32:48 -0400 Subject: [PATCH 29/55] Changelog for #10759, #11422, #11504, #12433 --- docs/release-notes/version-3.5.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 168f15fcb..ebf47800e 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -2,6 +2,12 @@ ## v3.5.1 (FUTURE) +## Enhancements + +* [#10759](https://github.com/netbox-community/netbox/issues/10759) - Support Markdown rendering for custom field descriptions +* [#11422](https://github.com/netbox-community/netbox/issues/11422) - Match on power panel name when searching for power feeds +* [#11504](https://github.com/netbox-community/netbox/issues/11504) - Add filter to select individual racks under rack elevations view + ### Bug Fixes * [#12367](https://github.com/netbox-community/netbox/issues/12367) - Fix `RelatedObjectDoesNotExist` exception under certain conditions (regression from #11550) @@ -15,6 +21,7 @@ * [#12410](https://github.com/netbox-community/netbox/issues/12410) - Fix base path for OpenAPI schema (fixes Swagger UI requests) * [#12412](https://github.com/netbox-community/netbox/issues/12412) - Device/VM interface MAC addresses can be nullified via REST API * [#12415](https://github.com/netbox-community/netbox/issues/12415) - Fix `ImportError` exception when running RQ worker +* [#12433](https://github.com/netbox-community/netbox/issues/12433) - Correct the application of URL query parameters for object list dashboard widgets * [#12436](https://github.com/netbox-community/netbox/issues/12436) - Remove extraneous "add" button from contact assignments list --- From 2c756873aad33e99127ebc66d0f0f961634a94f9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 4 May 2023 08:43:17 -0400 Subject: [PATCH 30/55] Fix broken links --- docs/development/models.md | 2 +- docs/models/dcim/platform.md | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/development/models.md b/docs/development/models.md index 6db61531b..d4838570a 100644 --- a/docs/development/models.md +++ b/docs/development/models.md @@ -32,7 +32,7 @@ These are considered the "core" application models which are used to model netwo * [circuits.Circuit](../models/circuits/circuit.md) * [circuits.Provider](../models/circuits/provider.md) -* [circuits.ProviderAccount](../models/circuits/provideracount.md) +* [circuits.ProviderAccount](../models/circuits/provideraccount.md) * [circuits.ProviderNetwork](../models/circuits/providernetwork.md) * [core.DataSource](../models/core/datasource.md) * [dcim.Cable](../models/dcim/cable.md) diff --git a/docs/models/dcim/platform.md b/docs/models/dcim/platform.md index 875f4a88a..dc332da74 100644 --- a/docs/models/dcim/platform.md +++ b/docs/models/dcim/platform.md @@ -4,8 +4,6 @@ A platform defines the type of software running on a [device](./device.md) or [v Platforms may optionally be limited by [manufacturer](./manufacturer.md): If a platform is assigned to a particular manufacturer, it can only be assigned to devices with a type belonging to that manufacturer. -The platform model is also used to indicate which [NAPALM driver](../../integrations/napalm.md) (if any) and any associated arguments NetBox should use when connecting to a remote device. The name of the driver along with optional parameters are stored with the platform. - The assignment of platforms to devices is an optional feature, and may be disregarded if not desired. ## Fields From 4df517e4da5fa8ff7db33bd3316eb7eb982a3713 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Thu, 4 May 2023 19:34:04 +0200 Subject: [PATCH 31/55] Fix #12464: Only set username, password for HTTP/s (#12482) --- netbox/core/data_backends.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/netbox/core/data_backends.py b/netbox/core/data_backends.py index d8424c223..085598372 100644 --- a/netbox/core/data_backends.py +++ b/netbox/core/data_backends.py @@ -31,6 +31,7 @@ def register_backend(name): """ Decorator for registering a DataBackend class. """ + def _wrapper(cls): registry['data_backends'][name] = cls return cls @@ -56,7 +57,6 @@ class DataBackend: @register_backend(DataSourceTypeChoices.LOCAL) class LocalBackend(DataBackend): - @contextmanager def fetch(self): logger.debug(f"Data source type is local; skipping fetch") @@ -71,12 +71,14 @@ class GitBackend(DataBackend): 'username': forms.CharField( required=False, label=_('Username'), - widget=forms.TextInput(attrs={'class': 'form-control'}) + widget=forms.TextInput(attrs={'class': 'form-control'}), + help_text=_("Only used for cloning with HTTP / HTTPS"), ), 'password': forms.CharField( required=False, label=_('Password'), - widget=forms.TextInput(attrs={'class': 'form-control'}) + widget=forms.TextInput(attrs={'class': 'form-control'}), + help_text=_("Only used for cloning with HTTP / HTTPS"), ), 'branch': forms.CharField( required=False, @@ -89,10 +91,22 @@ class GitBackend(DataBackend): def fetch(self): local_path = tempfile.TemporaryDirectory() - username = self.params.get('username') - password = self.params.get('password') - branch = self.params.get('branch') config = StackedConfig.default() + clone_args = { + "branch": self.params.get('branch'), + "config": config, + "depth": 1, + "errstream": porcelain.NoneStream(), + "quiet": True, + } + + if self.url_scheme in ('http', 'https'): + clone_args.update( + { + "username": self.params.get('username'), + "password": self.params.get('password'), + } + ) if settings.HTTP_PROXIES and self.url_scheme in ('http', 'https'): if proxy := settings.HTTP_PROXIES.get(self.url_scheme): @@ -100,10 +114,7 @@ class GitBackend(DataBackend): logger.debug(f"Cloning git repo: {self.url}") try: - porcelain.clone( - self.url, local_path.name, depth=1, branch=branch, username=username, password=password, - config=config, quiet=True, errstream=porcelain.NoneStream() - ) + porcelain.clone(self.url, local_path.name, **clone_args) except BaseException as e: raise SyncError(f"Fetching remote data failed ({type(e).__name__}): {e}") From 93b912c2da83c1f569df8abad9df6604116b4e21 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 4 May 2023 10:36:52 -0700 Subject: [PATCH 32/55] Adds ENGINE to database config (#12458) * adds ENGINE to database config #11791 * fixed lint issues * updated doc --- docs/configuration/required-parameters.md | 5 +++++ netbox/netbox/configuration_example.py | 1 + netbox/netbox/settings.py | 19 ++++++++++--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/configuration/required-parameters.md b/docs/configuration/required-parameters.md index c35f90f7b..1eba265bf 100644 --- a/docs/configuration/required-parameters.md +++ b/docs/configuration/required-parameters.md @@ -33,11 +33,13 @@ NetBox requires access to a PostgreSQL 11 or later database service to store dat * `HOST` - Name or IP address of the database server (use `localhost` if running locally) * `PORT` - TCP port of the PostgreSQL service; leave blank for default port (TCP/5432) * `CONN_MAX_AGE` - Lifetime of a [persistent database connection](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections), in seconds (300 is the default) +* `ENGINE` - The database backend to use; must be a PostgreSQL-compatible backend (e.g. `django.db.backends.postgresql`) Example: ```python DATABASE = { + 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'netbox', # Database name 'USER': 'netbox', # PostgreSQL username 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password @@ -50,6 +52,9 @@ DATABASE = { !!! 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). +!!! warning + 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. + --- ## REDIS diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py index 4878ec520..f415ca42f 100644 --- a/netbox/netbox/configuration_example.py +++ b/netbox/netbox/configuration_example.py @@ -13,6 +13,7 @@ ALLOWED_HOSTS = [] # PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: # https://docs.djangoproject.com/en/stable/ref/settings/#databases DATABASE = { + 'ENGINE': 'django.db.backends.postgresql', # Database engine 'NAME': 'netbox', # Database name 'USER': '', # PostgreSQL username 'PASSWORD': '', # PostgreSQL password diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 6608fcb8f..206b00fe7 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -182,15 +182,16 @@ if RELEASE_CHECK_URL: # Database # -# Only PostgreSQL is supported -if METRICS_ENABLED: - DATABASE.update({ - 'ENGINE': 'django_prometheus.db.backends.postgresql' - }) -else: - DATABASE.update({ - 'ENGINE': 'django.db.backends.postgresql' - }) +if 'ENGINE' not in DATABASE: + # Only PostgreSQL is supported + if METRICS_ENABLED: + DATABASE.update({ + 'ENGINE': 'django_prometheus.db.backends.postgresql' + }) + else: + DATABASE.update({ + 'ENGINE': 'django.db.backends.postgresql' + }) DATABASES = { 'default': DATABASE, From 25142e037ad84454f0ee4c10bdc59000447f545b Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 4 May 2023 10:43:40 -0700 Subject: [PATCH 33/55] Adds housekeeping systemd files (#12457) * adds housekeeping systemd files #11190 * Clean up Markdown --------- Co-authored-by: jeremystretch --- contrib/netbox-housekeeping.service | 17 +++++++++++++++ contrib/netbox-housekeeping.timer | 13 +++++++++++ docs/administration/housekeeping.md | 34 +++++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 contrib/netbox-housekeeping.service create mode 100644 contrib/netbox-housekeeping.timer diff --git a/contrib/netbox-housekeeping.service b/contrib/netbox-housekeeping.service new file mode 100644 index 000000000..4b0361fcb --- /dev/null +++ b/contrib/netbox-housekeeping.service @@ -0,0 +1,17 @@ +[Unit] +Description=NetBox Housekeeping Service +Documentation=https://docs.netbox.dev/ +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple + +User=netbox +Group=netbox +WorkingDirectory=/opt/netbox + +ExecStart=/opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py housekeeping + +[Install] +WantedBy=multi-user.target diff --git a/contrib/netbox-housekeeping.timer b/contrib/netbox-housekeeping.timer new file mode 100644 index 000000000..16facb05c --- /dev/null +++ b/contrib/netbox-housekeeping.timer @@ -0,0 +1,13 @@ +[Unit] +Description=NetBox Housekeeping Timer +Documentation=https://docs.netbox.dev/ +After=network-online.target +Wants=network-online.target + +[Timer] +OnCalendar=daily +AccuracySec=1h +Persistent=true + +[Install] +WantedBy=multi-user.target diff --git a/docs/administration/housekeeping.md b/docs/administration/housekeeping.md index 212b8308d..674ceb312 100644 --- a/docs/administration/housekeeping.md +++ b/docs/administration/housekeeping.md @@ -7,7 +7,13 @@ NetBox includes a `housekeeping` management command that should be run nightly. * Deleting job result records older than the configured [retention time](../configuration/miscellaneous.md#job_retention) * Check for new NetBox releases (if [`RELEASE_CHECK_URL`](../configuration/miscellaneous.md#release_check_url) is set) -This command can be invoked directly, or by using the shell script provided at `/opt/netbox/contrib/netbox-housekeeping.sh`. This script can be linked from your cron scheduler's daily jobs directory (e.g. `/etc/cron.daily`) or referenced directly within the cron configuration file. +This command can be invoked directly, or by using the shell script provided at `/opt/netbox/contrib/netbox-housekeeping.sh`. + +## Scheduling + +### Using Cron + +This script can be linked from your cron scheduler's daily jobs directory (e.g. `/etc/cron.daily`) or referenced directly within the cron configuration file. ```shell sudo ln -s /opt/netbox/contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-housekeeping @@ -16,4 +22,28 @@ sudo ln -s /opt/netbox/contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-hou !!! note On Debian-based systems, be sure to omit the `.sh` file extension when linking to the script from within a cron directory. Otherwise, the task may not run. -The `housekeeping` command can also be run manually at any time: Running the command outside scheduled execution times will not interfere with its operation. +### Using Systemd + +First, create symbolic links for the systemd service and timer files. Link the existing service and timer files from the `/opt/netbox/contrib/` directory to the `/etc/systemd/system/` directory: + +```bash +sudo ln -s /opt/netbox/contrib/netbox-housekeeping.service /etc/systemd/system/netbox-housekeeping.service +sudo ln -s /opt/netbox/contrib/netbox-housekeeping.timer /etc/systemd/system/netbox-housekeeping.timer +``` + +Then, reload the systemd configuration and enable the timer to start automatically at boot: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now netbox-housekeeping.timer +``` + +Check the status of your timer by running: + +```bash +sudo systemctl list-timers --all +``` + +This command will show a list of all timers, including your `netbox-housekeeping.timer`. Make sure the timer is active and properly scheduled. + +That's it! Your NetBox housekeeping service is now configured to run daily using systemd. From ea8a0135add064c5a69da7cbf2ff5c5c1828bac6 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Thu, 4 May 2023 10:46:31 -0700 Subject: [PATCH 34/55] Adds module status to module bay table (#12455) * adds module status to module bay table #11652 * removed the extra line * updated field name --- netbox/dcim/tables/devices.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 8a39ee16c..056d05c9a 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -39,6 +39,10 @@ __all__ = ( 'VirtualDeviceContextTable' ) +MODULEBAY_STATUS = """ +{% badge record.installed_module.get_status_display bg_color=record.installed_module.get_status_color %} +""" + def get_cabletermination_row_class(record): if record.mark_connected: @@ -781,14 +785,17 @@ class ModuleBayTable(DeviceComponentTable): tags = columns.TagColumn( url_name='dcim:modulebay_list' ) + module_status = columns.TemplateColumn( + template_code=MODULEBAY_STATUS + ) class Meta(DeviceComponentTable.Meta): model = models.ModuleBay fields = ( - 'pk', 'id', 'name', 'device', 'label', 'position', 'installed_module', 'module_serial', 'module_asset_tag', - 'description', 'tags', + 'pk', 'id', 'name', 'device', 'label', 'position', 'installed_module', 'module_status', 'module_serial', + 'module_asset_tag', 'description', 'tags', ) - default_columns = ('pk', 'name', 'device', 'label', 'installed_module', 'description') + default_columns = ('pk', 'name', 'device', 'label', 'installed_module', 'module_status', 'description') class DeviceModuleBayTable(ModuleBayTable): @@ -799,10 +806,10 @@ class DeviceModuleBayTable(ModuleBayTable): class Meta(DeviceComponentTable.Meta): model = models.ModuleBay fields = ( - 'pk', 'id', 'name', 'label', 'position', 'installed_module', 'module_serial', 'module_asset_tag', + 'pk', 'id', 'name', 'label', 'position', 'installed_module', 'module_status', 'module_serial', 'module_asset_tag', 'description', 'tags', 'actions', ) - default_columns = ('pk', 'name', 'label', 'installed_module', 'description') + default_columns = ('pk', 'name', 'label', 'installed_module', 'module_status', 'description') class InventoryItemTable(DeviceComponentTable): From 46914d9479f2af4b364c85e30f6835b31fd2ec2b Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Thu, 4 May 2023 12:59:28 -0500 Subject: [PATCH 35/55] Fixes: #10757 - Change IP interface assignment to use new selector (#12452) * Fixes: #10757 - Change interface assignment to use new selector. Perform the same change to the NAT assignment as well. * Remove nat_vrf from form and remove query_params that are not required anymore --- netbox/ipam/forms/model_forms.py | 62 +++-------------------- netbox/templates/ipam/ipaddress_edit.html | 56 -------------------- 2 files changed, 6 insertions(+), 112 deletions(-) diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index 9951b72e4..cf8117bf7 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -262,38 +262,21 @@ class IPRangeForm(TenancyForm, NetBoxModelForm): class IPAddressForm(TenancyForm, NetBoxModelForm): - device = DynamicModelChoiceField( - queryset=Device.objects.all(), - required=False, - initial_params={ - 'interfaces': '$interface' - } - ) interface = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, - query_params={ - 'device_id': '$device' - } - ) - virtual_machine = DynamicModelChoiceField( - queryset=VirtualMachine.objects.all(), - required=False, - initial_params={ - 'interfaces': '$vminterface' - } + selector=True, ) vminterface = DynamicModelChoiceField( queryset=VMInterface.objects.all(), required=False, + selector=True, label=_('Interface'), - query_params={ - 'virtual_machine_id': '$virtual_machine' - } ) fhrpgroup = DynamicModelChoiceField( queryset=FHRPGroup.objects.all(), required=False, + selector=True, label=_('FHRP Group') ) vrf = DynamicModelChoiceField( @@ -301,33 +284,11 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): required=False, label=_('VRF') ) - nat_device = DynamicModelChoiceField( - queryset=Device.objects.all(), - required=False, - selector=True, - label=_('Device') - ) - nat_virtual_machine = DynamicModelChoiceField( - queryset=VirtualMachine.objects.all(), - required=False, - selector=True, - label=_('Virtual Machine') - ) - nat_vrf = DynamicModelChoiceField( - queryset=VRF.objects.all(), - required=False, - selector=True, - label=_('VRF') - ) nat_inside = DynamicModelChoiceField( queryset=IPAddress.objects.all(), required=False, + selector=True, label=_('IP Address'), - query_params={ - 'device_id': '$nat_device', - 'virtual_machine_id': '$nat_virtual_machine', - 'vrf_id': '$nat_vrf', - } ) primary_for_parent = forms.BooleanField( required=False, @@ -338,8 +299,8 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): class Meta: model = IPAddress fields = [ - 'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'nat_device', 'nat_virtual_machine', - 'nat_vrf', 'nat_inside', 'tenant_group', 'tenant', 'description', 'comments', 'tags', + 'address', 'vrf', 'status', 'role', 'dns_name', 'primary_for_parent', 'nat_inside', 'tenant_group', + 'tenant', 'description', 'comments', 'tags', ] def __init__(self, *args, **kwargs): @@ -354,17 +315,6 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): initial['vminterface'] = instance.assigned_object elif type(instance.assigned_object) is FHRPGroup: initial['fhrpgroup'] = instance.assigned_object - if instance.nat_inside: - nat_inside_parent = instance.nat_inside.assigned_object - if type(nat_inside_parent) is Interface: - initial['nat_site'] = nat_inside_parent.device.site.pk - if nat_inside_parent.device.rack: - initial['nat_rack'] = nat_inside_parent.device.rack.pk - initial['nat_device'] = nat_inside_parent.device.pk - elif type(nat_inside_parent) is VMInterface: - if cluster := nat_inside_parent.virtual_machine.cluster: - initial['nat_cluster'] = cluster.pk - initial['nat_virtual_machine'] = nat_inside_parent.virtual_machine.pk kwargs['initial'] = initial super().__init__(*args, **kwargs) diff --git a/netbox/templates/ipam/ipaddress_edit.html b/netbox/templates/ipam/ipaddress_edit.html index 55c4c8a6b..419b32a79 100644 --- a/netbox/templates/ipam/ipaddress_edit.html +++ b/netbox/templates/ipam/ipaddress_edit.html @@ -56,11 +56,9 @@
- {% render_field form.device %} {% render_field form.interface %}
- {% render_field form.virtual_machine %} {% render_field form.vminterface %}
@@ -75,60 +73,6 @@
NAT IP (Inside)
-
- -
-
-
-
- {% render_field form.nat_device %} -
-
- {% render_field form.nat_virtual_machine %} -
-
- {% render_field form.nat_vrf %} -
{% render_field form.nat_inside %}
From 683ef30af48621a2502b4762d7cb07693426afe9 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 4 May 2023 11:05:32 -0700 Subject: [PATCH 36/55] 12363 update paragraph spacing on journal table (#12435) * 12363 update paragraph spacing on journal table * 12363 make css rule generic * 12363 change p tag to only effect last-child * 12363 change p table spacing to .5em * 12363 move comment --- netbox/project-static/dist/netbox-dark.css | Bin 375259 -> 375390 bytes netbox/project-static/dist/netbox-light.css | Bin 232636 -> 232699 bytes netbox/project-static/dist/netbox-print.css | Bin 727125 -> 727424 bytes netbox/project-static/styles/netbox.scss | 4 ++++ 4 files changed, 4 insertions(+) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index a690cfcac567f2f961bcaa36b88a810b54c79ee8..11110069e11515e95b985a5bb8cf2eb818eb395d 100644 GIT binary patch delta 65 zcmV-H0KWg*^A_Ip7J!5SgaU*Egaot&*k_Yr6DOBIWCaNea5`*Zb960ZXlZPdf%qkt Xz-I*smwi40K9?|M1qinUXa(0%Pp23p delta 35 rcmccjMeO!xv4$4L7N!>F7M3lnH`1qn2xL;3Zjj2#vAsQm^?D!x88i-Q diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index fad60f89bd494d1771dd39d750e056aeda843cba..8a3c83af9b64aa1f2bd4a5ffab0b40139bf9dc80 100644 GIT binary patch delta 68 zcmdlplkfLTzJ?aY7N#xCg`ui?rm4BLC5cHnsR|`23I$d Date: Thu, 4 May 2023 14:14:21 -0400 Subject: [PATCH 37/55] Changelog for #10757, #11652, #11791, #11190, #12363, #12464 --- docs/release-notes/version-3.5.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index ebf47800e..1c0206041 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -7,9 +7,14 @@ * [#10759](https://github.com/netbox-community/netbox/issues/10759) - Support Markdown rendering for custom field descriptions * [#11422](https://github.com/netbox-community/netbox/issues/11422) - Match on power panel name when searching for power feeds * [#11504](https://github.com/netbox-community/netbox/issues/11504) - Add filter to select individual racks under rack elevations view +* [#11652](https://github.com/netbox-community/netbox/issues/11652) - Add a module status column to module bay tables +* [#11791](https://github.com/netbox-community/netbox/issues/11791) - Enable configuration of custom database backend via `ENGINE` parameter +* [#11190](https://github.com/netbox-community/netbox/issues/11190) - Including systemd service & timer configurations for housekeeping tasks ### Bug Fixes +* [#10757](https://github.com/netbox-community/netbox/issues/10757) - Simplify IP address interface and NAT IP assignment form fields to avoid confusion +* [#12363](https://github.com/netbox-community/netbox/issues/12363) - Fix whitespace for paragraph elements in Markdown-rendered table columns * [#12367](https://github.com/netbox-community/netbox/issues/12367) - Fix `RelatedObjectDoesNotExist` exception under certain conditions (regression from #11550) * [#12380](https://github.com/netbox-community/netbox/issues/12380) - Allow selecting object change as model under object list widget configuration * [#12384](https://github.com/netbox-community/netbox/issues/12384) - Add a three-second timeout for RSS reader widget @@ -23,6 +28,7 @@ * [#12415](https://github.com/netbox-community/netbox/issues/12415) - Fix `ImportError` exception when running RQ worker * [#12433](https://github.com/netbox-community/netbox/issues/12433) - Correct the application of URL query parameters for object list dashboard widgets * [#12436](https://github.com/netbox-community/netbox/issues/12436) - Remove extraneous "add" button from contact assignments list +* [#12464](https://github.com/netbox-community/netbox/issues/12464) - Apply credentials for git data source only when connecting via HTTP/S --- From f3826e623580f45e360fa3190b149f208b466324 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 4 May 2023 11:29:25 -0700 Subject: [PATCH 38/55] fix runscript command --- netbox/extras/management/commands/runscript.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/management/commands/runscript.py b/netbox/extras/management/commands/runscript.py index 76ceeb239..b42e9b47d 100644 --- a/netbox/extras/management/commands/runscript.py +++ b/netbox/extras/management/commands/runscript.py @@ -111,7 +111,7 @@ class Command(BaseCommand): # Create the job job = Job.objects.create( - instance=module, + object=module, name=script.name, user=User.objects.filter(is_superuser=True).order_by('pk')[0], job_id=uuid.uuid4() From ab69faab87ea3215638be887915043404904c339 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Thu, 4 May 2023 20:04:14 +0200 Subject: [PATCH 39/55] Fix #12483: Using mutable dulwich configuration --- netbox/core/data_backends.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/core/data_backends.py b/netbox/core/data_backends.py index 085598372..6cc534774 100644 --- a/netbox/core/data_backends.py +++ b/netbox/core/data_backends.py @@ -12,7 +12,7 @@ from django import forms from django.conf import settings from django.utils.translation import gettext as _ from dulwich import porcelain -from dulwich.config import StackedConfig +from dulwich.config import ConfigDict from netbox.registry import registry from .choices import DataSourceTypeChoices @@ -91,7 +91,7 @@ class GitBackend(DataBackend): def fetch(self): local_path = tempfile.TemporaryDirectory() - config = StackedConfig.default() + config = ConfigDict() clone_args = { "branch": self.params.get('branch'), "config": config, From a96b76a8d1affbd275643753fb23b93194bf2618 Mon Sep 17 00:00:00 2001 From: Patrick Hurrelmann Date: Fri, 5 May 2023 10:10:36 +0200 Subject: [PATCH 40/55] Fixes #12496: Remove account field from provider template --- netbox/templates/circuits/provider.html | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/netbox/templates/circuits/provider.html b/netbox/templates/circuits/provider.html index 695202176..5a565ea29 100644 --- a/netbox/templates/circuits/provider.html +++ b/netbox/templates/circuits/provider.html @@ -29,17 +29,6 @@ {% endfor %} - - - Account - - {{ object.account|placeholder }} - Description {{ object.description|placeholder }} From 9d62174e1e07e5afdd39dd8a9904dab61a91eef5 Mon Sep 17 00:00:00 2001 From: Luke Anderson Date: Fri, 5 May 2023 12:05:48 +0930 Subject: [PATCH 41/55] Fix #12245 - Add WLAN to InterfaceBulkEditForm --- netbox/dcim/forms/bulk_edit.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index d5abce647..6ed483c79 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -13,6 +13,7 @@ from tenancy.models import Tenant from utilities.forms import BulkEditForm, add_blank_choice, form_from_model from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions +from wireless.models import WirelessLAN, WirelessLANGroup __all__ = ( 'CableBulkEditForm', @@ -1139,7 +1140,7 @@ class InterfaceBulkEditForm( form_from_model(Interface, [ 'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', - 'tx_power', + 'tx_power', 'wireless_lans' ]), ComponentBulkEditForm ): @@ -1229,6 +1230,19 @@ class InterfaceBulkEditForm( required=False, label=_('VRF') ) + wireless_lan_group = DynamicModelChoiceField( + queryset=WirelessLANGroup.objects.all(), + required=False, + label=_('Wireless LAN group') + ) + wireless_lans = DynamicModelMultipleChoiceField( + queryset=WirelessLAN.objects.all(), + required=False, + label=_('Wireless LANs'), + query_params={ + 'group_id': '$wireless_lan_group', + } + ) model = Interface fieldsets = ( @@ -1238,12 +1252,14 @@ class InterfaceBulkEditForm( ('PoE', ('poe_mode', 'poe_type')), ('Related Interfaces', ('parent', 'bridge', 'lag')), ('802.1Q Switching', ('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans')), - ('Wireless', ('rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width')), + ('Wireless', ( + 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans', + )), ) nullable_fields = ( 'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'vdcs', 'mtu', 'description', 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', - 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vrf', + 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vrf', 'wireless_lans' ) def __init__(self, *args, **kwargs): From abdcfdecf5f62917bc46d95c6d32699b4bf38e52 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Fri, 5 May 2023 06:09:21 -0700 Subject: [PATCH 42/55] Adds description to elevation device tooltip (#12488) * adds description to elevation device tooltip #11801 * changes as per review * changes as per review * Rearrange attrs, add headings, and update docstring --------- Co-authored-by: jeremystretch --- netbox/dcim/svg/racks.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/netbox/dcim/svg/racks.py b/netbox/dcim/svg/racks.py index 6c57e6023..62878cef9 100644 --- a/netbox/dcim/svg/racks.py +++ b/netbox/dcim/svg/racks.py @@ -37,15 +37,28 @@ def get_device_name(device): def get_device_description(device): - return '{} ({}) — {} {} ({}U) {} {}'.format( - device.name, - device.device_role, - device.device_type.manufacturer.name, - device.device_type.model, - floatformat(device.device_type.u_height), - device.asset_tag or '', - device.serial or '' - ) + """ + Return a description for a device to be rendered in the rack elevation in the following format + + Name: + Role: + Device Type: () + Asset tag: (if defined) + Serial: (if defined) + Description: (if defined) + """ + description = f'Name: {device.name}' + description += f'\nRole: {device.device_role}' + u_height = f'{floatformat(device.device_type.u_height)}U' + description += f'\nDevice Type: {device.device_type.manufacturer.name} {device.device_type.model} ({u_height})' + if device.asset_tag: + description += f'\nAsset tag: {device.asset_tag}' + if device.serial: + description += f'\nSerial: {device.serial}' + if device.description: + description += f'\nDescription: {device.description}' + + return description class RackElevationSVG: From 7a38f601de8d3021242b6ada0de495ab7920d554 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Fri, 5 May 2023 08:35:05 -0500 Subject: [PATCH 43/55] Fixes: #11715 - Fix Parent Prefix table display (#12448) * Fixes: #11715 - Fix Parent Prefix table display of global vrf prefixes that are **not** containers. * Combine AND into a single Q object Co-authored-by: Jeremy Stretch --------- Co-authored-by: Jeremy Stretch --- netbox/ipam/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index a49c4aab3..93d0dc8bb 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -14,6 +14,7 @@ from utilities.views import ViewTab, register_model_view from virtualization.filtersets import VMInterfaceFilterSet from virtualization.models import VMInterface from . import filtersets, forms, tables +from .choices import PrefixStatusChoices from .constants import * from .models import * from .tables.l2vpn import L2VPNTable, L2VPNTerminationTable @@ -495,7 +496,7 @@ class PrefixView(generic.ObjectView): # Parent prefixes table parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter( - Q(vrf=instance.vrf) | Q(vrf__isnull=True) + Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER) ).filter( prefix__net_contains=str(instance.prefix) ).prefetch_related( From 9909213c0d62ec07bdeb3cb9915b6429e46b7d6d Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 5 May 2023 06:37:38 -0700 Subject: [PATCH 44/55] 12416 warning for missing script file (#12456) * 12416 warning for missing script file * 12416 widen exception catching for internal script error * Update netbox/extras/models/scripts.py Co-authored-by: Jeremy Stretch * 12416 update from review feedback --------- Co-authored-by: Jeremy Stretch --- netbox/extras/models/scripts.py | 10 ++- netbox/extras/views.py | 1 - netbox/templates/extras/script_list.html | 80 +++++++++++++----------- 3 files changed, 52 insertions(+), 39 deletions(-) diff --git a/netbox/extras/models/scripts.py b/netbox/extras/models/scripts.py index 1a7559e53..de48aae8e 100644 --- a/netbox/extras/models/scripts.py +++ b/netbox/extras/models/scripts.py @@ -1,4 +1,5 @@ import inspect +import logging from functools import cached_property from django.db import models @@ -16,6 +17,8 @@ __all__ = ( 'ScriptModule', ) +logger = logging.getLogger('netbox.data_backends') + class Script(WebhooksMixin, models.Model): """ @@ -53,7 +56,12 @@ class ScriptModule(PythonModuleMixin, JobsMixin, ManagedFile): # For child objects in submodules use the full import path w/o the root module as the name return cls.full_name.split(".", maxsplit=1)[1] - module = self.get_module() + try: + module = self.get_module() + except Exception as e: + logger.debug(f"Failed to load script: {self.python_name} error: {e}") + module = None + scripts = {} ordered = getattr(module, 'script_order', []) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 286ec76cd..c7b7793a0 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -1033,7 +1033,6 @@ class ScriptView(ContentTypePermissionRequiredMixin, View): return 'extras.view_script' def get(self, request, module, name): - print(module) module = get_object_or_404(ScriptModule.objects.restrict(request.user), file_path__startswith=module) script = module.scripts[name]() form = script.as_form(initial=normalize_querydict(request.GET)) diff --git a/netbox/templates/extras/script_list.html b/netbox/templates/extras/script_list.html index bccbce589..9a67e2b10 100644 --- a/netbox/templates/extras/script_list.html +++ b/netbox/templates/extras/script_list.html @@ -37,43 +37,49 @@
{% include 'inc/sync_warning.html' with object=module %} - - - - - - - - - - - {% with jobs=module.get_latest_jobs %} - {% for script_name, script_class in module.scripts.items %} - - - - {% with last_result=jobs|get_key:script_class.name %} - {% if last_result %} - - - {% else %} - - - {% endif %} - {% endwith %} - - {% endfor %} - {% endwith %} - -
NameDescriptionLast RunStatus
- {{ script_class.name }} - - {{ script_class.Meta.description|markdown|placeholder }} - - {{ last_result.created|annotated_date }} - - {% badge last_result.get_status_display last_result.get_status_color %} - Never{{ ''|placeholder }}
+ {% if not module.scripts %} + + {% else %} + + + + + + + + + + + {% with jobs=module.get_latest_jobs %} + {% for script_name, script_class in module.scripts.items %} + + + + {% with last_result=jobs|get_key:script_class.name %} + {% if last_result %} + + + {% else %} + + + {% endif %} + {% endwith %} + + {% endfor %} + {% endwith %} + +
NameDescriptionLast RunStatus
+ {{ script_class.name }} + + {{ script_class.Meta.description|markdown|placeholder }} + + {{ last_result.created|annotated_date }} + + {% badge last_result.get_status_display last_result.get_status_color %} + Never{{ ''|placeholder }}
+ {% endif %}
{% empty %} From 42346702a1873b8670dd67daab209a6657b69c03 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Fri, 5 May 2023 06:48:13 -0700 Subject: [PATCH 45/55] Adds image attachment list view (#12487) * adds image attachment list view #11932 * fixed typo * Update netbox/extras/tables/tables.py Co-authored-by: Jeremy Stretch * Update netbox/extras/forms/filtersets.py Co-authored-by: Jeremy Stretch * changes as per review * Disable ordering by size (not stored in database) --------- Co-authored-by: Jeremy Stretch --- netbox/extras/forms/filtersets.py | 17 +++++++- netbox/extras/tables/tables.py | 23 +++++++++++ netbox/extras/urls.py | 1 + netbox/extras/views.py | 8 ++++ netbox/netbox/navigation/menu.py | 1 + .../inc/panels/image_attachments.html | 41 ++----------------- 6 files changed, 52 insertions(+), 39 deletions(-) diff --git a/netbox/extras/forms/filtersets.py b/netbox/extras/forms/filtersets.py index 056302343..fae15d041 100644 --- a/netbox/extras/forms/filtersets.py +++ b/netbox/extras/forms/filtersets.py @@ -11,7 +11,7 @@ from extras.utils import FeatureQuery from netbox.forms.base import NetBoxModelFilterSetForm from tenancy.models import Tenant, TenantGroup from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice -from utilities.forms.fields import ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField +from utilities.forms.fields import ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.widgets import APISelectMultiple, DateTimePicker from virtualization.models import Cluster, ClusterGroup, ClusterType from .mixins import SavedFiltersMixin @@ -22,6 +22,7 @@ __all__ = ( 'CustomFieldFilterForm', 'CustomLinkFilterForm', 'ExportTemplateFilterForm', + 'ImageAttachmentFilterForm', 'JournalEntryFilterForm', 'LocalConfigContextFilterForm', 'ObjectChangeFilterForm', @@ -137,6 +138,20 @@ class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm): ) +class ImageAttachmentFilterForm(SavedFiltersMixin, FilterForm): + fieldsets = ( + (None, ('q', 'filter_id')), + ('Attributes', ('content_type_id', 'name',)), + ) + content_type_id = ContentTypeChoiceField( + queryset=ContentType.objects.filter(FeatureQuery('custom_fields').get_query()), + required=False + ) + name = forms.CharField( + required=False + ) + + class SavedFilterFilterForm(SavedFiltersMixin, FilterForm): fieldsets = ( (None, ('q', 'filter_id')), diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 59b45c059..e6d014302 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -13,6 +13,7 @@ __all__ = ( 'CustomFieldTable', 'CustomLinkTable', 'ExportTemplateTable', + 'ImageAttachmentTable', 'JournalEntryTable', 'ObjectChangeTable', 'SavedFilterTable', @@ -86,6 +87,28 @@ class ExportTemplateTable(NetBoxTable): ) +class ImageAttachmentTable(NetBoxTable): + id = tables.Column( + linkify=False + ) + content_type = columns.ContentTypeColumn() + parent = tables.Column( + linkify=True + ) + size = tables.Column( + orderable=False, + verbose_name='Size (bytes)' + ) + + class Meta(NetBoxTable.Meta): + model = ImageAttachment + fields = ( + 'pk', 'content_type', 'parent', 'image', 'name', 'image_height', 'image_width', 'size', 'created', + 'last_updated', + ) + default_columns = ('content_type', 'parent', 'image', 'name', 'size', 'created') + + class SavedFilterTable(NetBoxTable): name = tables.Column( linkify=True diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py index f04c53add..e516da091 100644 --- a/netbox/extras/urls.py +++ b/netbox/extras/urls.py @@ -73,6 +73,7 @@ urlpatterns = [ path('config-templates//', include(get_model_urls('extras', 'configtemplate'))), # Image attachments + path('image-attachments/', views.ImageAttachmentListView.as_view(), name='imageattachment_list'), path('image-attachments/add/', views.ImageAttachmentEditView.as_view(), name='imageattachment_add'), path('image-attachments//', include(get_model_urls('extras', 'imageattachment'))), diff --git a/netbox/extras/views.py b/netbox/extras/views.py index c7b7793a0..e695c39f0 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -577,6 +577,14 @@ class ObjectChangeView(generic.ObjectView): # Image attachments # +class ImageAttachmentListView(generic.ObjectListView): + queryset = ImageAttachment.objects.all() + filterset = filtersets.ImageAttachmentFilterSet + filterset_form = forms.ImageAttachmentFilterForm + table = tables.ImageAttachmentTable + actions = ('export',) + + @register_model_view(ImageAttachment, 'edit') class ImageAttachmentEditView(generic.ObjectEditView): queryset = ImageAttachment.objects.all() diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 2b1428d27..b96c275ea 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -292,6 +292,7 @@ CUSTOMIZATION_MENU = Menu( get_model_item('extras', 'exporttemplate', _('Export Templates')), get_model_item('extras', 'savedfilter', _('Saved Filters')), get_model_item('extras', 'tag', 'Tags'), + get_model_item('extras', 'imageattachment', _('Image Attachments'), actions=()), ), ), MenuGroup( diff --git a/netbox/templates/inc/panels/image_attachments.html b/netbox/templates/inc/panels/image_attachments.html index 9706a7ffe..0c1d212d9 100644 --- a/netbox/templates/inc/panels/image_attachments.html +++ b/netbox/templates/inc/panels/image_attachments.html @@ -4,44 +4,9 @@
Images
-
- {% with images=object.images.all %} - {% if images.exists %} - - - - - - - - {% for attachment in images %} - - - - - - - {% endfor %} -
NameSizeCreated
- - {{ attachment }} - {{ attachment.size|filesizeformat }}{{ attachment.created|annotated_date }} - {% if perms.extras.change_imageattachment %} - - - - {% endif %} - {% if perms.extras.delete_imageattachment %} - - - - {% endif %} -
- {% else %} -
None
- {% endif %} - {% endwith %} -
+
{% if perms.extras.add_imageattachment %}