From 25dc7e234dcb11ee230fed82356c8716a36538f1 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 27 Apr 2023 14:52:07 -0400 Subject: [PATCH 01/43] 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 02/43] 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 03/43] 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 04/43] 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 05/43] 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 06/43] 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 07/43] 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 08/43] 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 09/43] 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 10/43] 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 11/43] 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 12/43] 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 13/43] #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 14/43] 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 15/43] 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 16/43] 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 17/43] 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 18/43] 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 19/43] 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 20/43] 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 21/43] 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 22/43] 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 23/43] 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 24/43] 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 25/43] 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 26/43] 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 27/43] 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 28/43] 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 29/43] 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 30/43] 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 31/43] 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 32/43] 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 33/43] 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 34/43] 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 35/43] 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 36/43] 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 37/43] 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 38/43] 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 %}