Merge pull request #17478 from netbox-community/develop

Release v4.1.1
This commit is contained in:
Jeremy Stretch 2024-09-12 14:51:13 -04:00 committed by GitHub
commit 0e34fba922
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
118 changed files with 56655 additions and 48497 deletions

View File

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

View File

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

View File

@ -45,6 +45,7 @@ jobs:
This PR has been automatically closed due to lack of activity.
days-before-pr-stale: 15
days-before-pr-close: 15
exempt-pr-labels: 'status: blocked'
stale-pr-label: 'pending closure'
stale-pr-message: >
This PR has been automatically marked as stale because it has not had

View File

@ -84,10 +84,6 @@ Jinja2
# https://python-markdown.github.io/changelog/
Markdown
# File inclusion plugin for Python-Markdown
# https://github.com/cmacmackin/markdown-include
markdown-include
# MkDocs Material theme (for documentation build)
# https://squidfunk.github.io/mkdocs-material/changelog/
mkdocs-material

View File

@ -20,7 +20,7 @@
Alias /static /opt/netbox/netbox/static
<Directory /opt/netbox/netbox/static>
Options Indexes FollowSymLinks MultiViews
Options FollowSymLinks MultiViews
AllowOverride None
Require all granted
</Directory>

View File

@ -149,6 +149,7 @@
"nema-l15-60p",
"nema-l21-20p",
"nema-l21-30p",
"nema-l22-20p",
"nema-l22-30p",
"cs6361c",
"cs6365c",
@ -262,6 +263,7 @@
"nema-l15-60r",
"nema-l21-20r",
"nema-l21-30r",
"nema-l22-20r",
"nema-l22-30r",
"CS6360C",
"CS6364C",
@ -518,6 +520,14 @@
"urm-p4",
"urm-p8",
"splice",
"usb-a",
"usb-b",
"usb-c",
"usb-mini-a",
"usb-mini-b",
"usb-micro-a",
"usb-micro-b",
"usb-micro-ab",
"other"
]
}
@ -575,6 +585,14 @@
"urm-p4",
"urm-p8",
"splice",
"usb-a",
"usb-b",
"usb-c",
"usb-mini-a",
"usb-mini-b",
"usb-micro-a",
"usb-micro-b",
"usb-micro-ab",
"other"
]
}

View File

@ -17,6 +17,9 @@ They can also be used as a mechanism for validating the integrity of data within
Custom scripts are Python code which exists outside the NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're completely custom, there is no inherent limitation on what a script can accomplish.
!!! danger "Only install trusted scripts"
Custom scripts have unrestricted access to change anything in the databse and are inherently unsafe and should only be installed and run from trusted sources. You should also review and set permissions for who can run scripts if the script can modify any data.
## Writing Custom Scripts
All custom scripts must inherit from the `extras.scripts.Script` base class. This class provides the functionality necessary to generate forms and log activity.

View File

@ -39,6 +39,10 @@ mkdocs serve
Follow these instructions to perform a new installation of NetBox in a temporary environment. This process must not be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release.
### Test Upgrade Paths
Upgrading from a previous version typically involves database migrations, which must work without errors. Supported upgrade paths include from one minor version to another within the same major version (i.e. 4.0 to 4.1), as well as from the latest patch version of the previous minor version (i.e. 3.7 to 4.0 or to 4.1). Prior to release, test all these supported paths by loading demo data from the source version and performing a `./manage.py migrate`.
### Merge the Release Branch
Submit a pull request to merge the `feature` branch into the `develop` branch in preparation for its release. Once it has been merged, continue with the section for patch releases below.

View File

@ -1,5 +1,31 @@
# NetBox v4.1
## v4.1.1 (2024-09-12)
### Enhancements
* [#16926](https://github.com/netbox-community/netbox/issues/16926) - Add USB front & rear port types
* [#17347](https://github.com/netbox-community/netbox/issues/17347) - Add NEMA L22-20 power port & outlet types
### Bug Fixes
* [#17066](https://github.com/netbox-community/netbox/issues/17066) - Fix OpenAPI schema definition for custom scripts REST API endpoint
* [#17332](https://github.com/netbox-community/netbox/issues/17332) - Restore pagination for object list dashboard widgets
* [#17333](https://github.com/netbox-community/netbox/issues/17333) - Avoid prefetching all jobs when retrieving custom scripts via the REST API
* [#17353](https://github.com/netbox-community/netbox/issues/17353) - Fix styling of map buttons under site and device views
* [#17354](https://github.com/netbox-community/netbox/issues/17354) - Prevent object & multi-object custom fields from breaking bulk import forms
* [#17362](https://github.com/netbox-community/netbox/issues/17362) - Remove duplicate prefixes & IP addresses returned by the `present_in_vrf` query filter
* [#17364](https://github.com/netbox-community/netbox/issues/17364) - Fix rendering of Markdown tables inside object list dashboard widgets
* [#17387](https://github.com/netbox-community/netbox/issues/17387) - Fix display of the changelog tab for users with sufficient permission
* [#17410](https://github.com/netbox-community/netbox/issues/17410) - Enable debug toolbar middleware for `strawberry-django` only when `DEBUG` is true
* [#17414](https://github.com/netbox-community/netbox/issues/17414) - Fix support for declaring individual VLAN IDs within a VLAN group
* [#17431](https://github.com/netbox-community/netbox/issues/17431) - Fix database migration error when upgrading to v4.1 from v3.7 or earlier
* [#17437](https://github.com/netbox-community/netbox/issues/17437) - Fix exception when specifying a bridge relationship on an interface template
* [#17444](https://github.com/netbox-community/netbox/issues/17444) - Custom script fails to execute when triggered by an event rule
* [#17457](https://github.com/netbox-community/netbox/issues/17457) - GraphQL `service_list` filter should not require a port number
---
## v4.1.0 (2024-09-03)
### Breaking Changes

View File

@ -72,7 +72,7 @@ class NestedInterfaceTemplateSerializer(WritableNestedSerializer):
class Meta:
model = models.InterfaceTemplate
fields = ['id', 'url', 'display_url', 'display', 'name']
fields = ['id', 'url', 'display', 'name']
class NestedDeviceBaySerializer(WritableNestedSerializer):

View File

@ -396,6 +396,7 @@ class PowerPortTypeChoices(ChoiceSet):
TYPE_NEMA_L1560P = 'nema-l15-60p'
TYPE_NEMA_L2120P = 'nema-l21-20p'
TYPE_NEMA_L2130P = 'nema-l21-30p'
TYPE_NEMA_L2220P = 'nema-l22-20p'
TYPE_NEMA_L2230P = 'nema-l22-30p'
# California style
TYPE_CS6361C = 'cs6361c'
@ -517,6 +518,7 @@ class PowerPortTypeChoices(ChoiceSet):
(TYPE_NEMA_L1560P, 'NEMA L15-60P'),
(TYPE_NEMA_L2120P, 'NEMA L21-20P'),
(TYPE_NEMA_L2130P, 'NEMA L21-30P'),
(TYPE_NEMA_L2220P, 'NEMA L22-20P'),
(TYPE_NEMA_L2230P, 'NEMA L22-30P'),
)),
(_('California Style'), (
@ -649,6 +651,7 @@ class PowerOutletTypeChoices(ChoiceSet):
TYPE_NEMA_L1560R = 'nema-l15-60r'
TYPE_NEMA_L2120R = 'nema-l21-20r'
TYPE_NEMA_L2130R = 'nema-l21-30r'
TYPE_NEMA_L2220R = 'nema-l22-20r'
TYPE_NEMA_L2230R = 'nema-l22-30r'
# California style
TYPE_CS6360C = 'CS6360C'
@ -763,6 +766,7 @@ class PowerOutletTypeChoices(ChoiceSet):
(TYPE_NEMA_L1560R, 'NEMA L15-60R'),
(TYPE_NEMA_L2120R, 'NEMA L21-20R'),
(TYPE_NEMA_L2130R, 'NEMA L21-30R'),
(TYPE_NEMA_L2220R, 'NEMA L22-20R'),
(TYPE_NEMA_L2230R, 'NEMA L22-30R'),
)),
(_('California Style'), (
@ -1347,6 +1351,14 @@ class PortTypeChoices(ChoiceSet):
TYPE_URM_P2 = 'urm-p2'
TYPE_URM_P4 = 'urm-p4'
TYPE_URM_P8 = 'urm-p8'
TYPE_USB_A = 'usb-a'
TYPE_USB_B = 'usb-b'
TYPE_USB_C = 'usb-c'
TYPE_USB_MINI_A = 'usb-mini-a'
TYPE_USB_MINI_B = 'usb-mini-b'
TYPE_USB_MICRO_A = 'usb-micro-a'
TYPE_USB_MICRO_B = 'usb-micro-b'
TYPE_USB_MICRO_AB = 'usb-micro-ab'
TYPE_OTHER = 'other'
CHOICES = (
@ -1406,6 +1418,19 @@ class PortTypeChoices(ChoiceSet):
(TYPE_SPLICE, 'Splice'),
),
),
(
_('USB'),
(
(TYPE_USB_A, 'USB Type A'),
(TYPE_USB_B, 'USB Type B'),
(TYPE_USB_C, 'USB Type C'),
(TYPE_USB_MINI_A, 'USB Mini A'),
(TYPE_USB_MINI_B, 'USB Mini B'),
(TYPE_USB_MICRO_A, 'USB Micro A'),
(TYPE_USB_MICRO_B, 'USB Micro B'),
(TYPE_USB_MICRO_AB, 'USB Micro AB'),
),
),
(
_('Other'),
(
@ -1444,6 +1469,7 @@ class CableTypeChoices(ChoiceSet):
TYPE_SMF_OS2 = 'smf-os2'
TYPE_AOC = 'aoc'
TYPE_POWER = 'power'
TYPE_USB = 'usb'
CHOICES = (
(
@ -1476,6 +1502,7 @@ class CableTypeChoices(ChoiceSet):
(TYPE_AOC, 'Active Optical Cabling (AOC)'),
),
),
(TYPE_USB, _('USB')),
(TYPE_POWER, _('Power')),
)

View File

@ -975,8 +975,8 @@ class InterfaceTemplateForm(ModularComponentTemplateForm):
queryset=InterfaceTemplate.objects.all(),
required=False,
query_params={
'devicetype_id': '$device_type',
'moduletype_id': '$module_type',
'device_type_id': '$device_type',
'module_type_id': '$module_type',
}
)

View File

@ -2,6 +2,7 @@ from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils.module_loading import import_string
from django_rq.queues import get_connection
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
@ -229,9 +230,13 @@ class ConfigTemplateViewSet(SyncedDataMixin, ConfigTemplateRenderMixin, NetBoxMo
# Scripts
#
@extend_schema_view(
update=extend_schema(request=serializers.ScriptInputSerializer),
partial_update=extend_schema(request=serializers.ScriptInputSerializer),
)
class ScriptViewSet(ModelViewSet):
permission_classes = [IsAuthenticatedOrLoginNotRequired]
queryset = Script.objects.prefetch_related('jobs')
queryset = Script.objects.all()
serializer_class = serializers.ScriptSerializer
filterset_class = filtersets.ScriptFilterSet

View File

@ -525,23 +525,29 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
elif self.type == CustomFieldTypeChoices.TYPE_OBJECT:
model = self.related_object_type.model_class()
field_class = CSVModelChoiceField if for_csv_import else DynamicModelChoiceField
field = field_class(
queryset=model.objects.all(),
required=required,
initial=initial,
query_params=self.related_object_filter
)
kwargs = {
'queryset': model.objects.all(),
'required': required,
'initial': initial,
}
if not for_csv_import:
kwargs['query_params'] = self.related_object_filter
field = field_class(**kwargs)
# Multiple objects
elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
model = self.related_object_type.model_class()
field_class = CSVModelMultipleChoiceField if for_csv_import else DynamicModelMultipleChoiceField
field = field_class(
queryset=model.objects.all(),
required=required,
initial=initial,
query_params=self.related_object_filter
)
kwargs = {
'queryset': model.objects.all(),
'required': required,
'initial': initial,
}
if not for_csv_import:
kwargs['query_params'] = self.related_object_filter
field = field_class(**kwargs)
# Text
else:

View File

@ -458,7 +458,7 @@ class PrefixFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
return queryset.filter(
Q(vrf=vrf) |
Q(vrf__export_targets__in=vrf.import_targets.all())
)
).distinct()
class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
@ -738,7 +738,7 @@ class IPAddressFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
return queryset.filter(
Q(vrf=vrf) |
Q(vrf__export_targets__in=vrf.import_targets.all())
)
).distinct()
def filter_device(self, queryset, name, value):
devices = Device.objects.filter(**{'{}__in'.format(name): value})

View File

@ -100,7 +100,7 @@ class VLANGroup(OrganizationalModel):
if self.vid_ranges and check_ranges_overlap(self.vid_ranges):
raise ValidationError({'vid_ranges': _("Ranges cannot overlap.")})
for vid_range in self.vid_ranges:
if vid_range.lower >= vid_range.upper:
if vid_range.lower > vid_range.upper:
raise ValidationError({
'vid_ranges': _(
"Maximum child VID must be greater than or equal to minimum child VID ({value})"

View File

@ -543,3 +543,17 @@ class TestVLANGroup(TestCase):
vlan = VLAN(vid=109, name='VLAN 109', group=vlangroup)
vlan.full_clean()
def test_overlapping_vlan(self):
vlangroup = VLANGroup(
name='VLAN Group 1',
slug='vlan-group-1',
vid_ranges=string_to_ranges('2-4,3-5'),
)
with self.assertRaises(ValidationError):
vlangroup.full_clean()
# make sure single vlan range works
vlangroup.vid_ranges = string_to_ranges('2-2')
vlangroup.full_clean()
vlangroup.save()

View File

@ -47,7 +47,7 @@ def map_strawberry_type(field):
pass
elif isinstance(field, NumericArrayFilter):
should_create_function = True
attr_type = int
attr_type = int | None
elif isinstance(field, TreeNodeMultipleChoiceFilter):
should_create_function = True
attr_type = List[str] | None

View File

@ -100,7 +100,8 @@ class JobRunner(ABC):
This method is a wrapper of `Job.enqueue()` using `handle()` as function callback. See its documentation for
parameters.
"""
return Job.enqueue(cls.handle, name=cls.name, *args, **kwargs)
name = kwargs.pop('name', None) or cls.name
return Job.enqueue(cls.handle, name=name, *args, **kwargs)
@classmethod
@advisory_lock(ADVISORY_LOCK_KEYS['job-schedules'])

View File

@ -416,7 +416,6 @@ if not DJANGO_ADMIN_ENABLED:
# Middleware
MIDDLEWARE = [
"strawberry_django.middlewares.debug_toolbar.DebugToolbarMiddleware",
'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
@ -431,6 +430,13 @@ MIDDLEWARE = [
'netbox.middleware.CoreMiddleware',
'netbox.middleware.MaintenanceModeMiddleware',
]
if DEBUG:
MIDDLEWARE = [
"strawberry_django.middlewares.debug_toolbar.DebugToolbarMiddleware",
*MIDDLEWARE,
]
if METRICS_ENABLED:
# If metrics are enabled, add the before & after Prometheus middleware
MIDDLEWARE = [

View File

@ -38,7 +38,7 @@ class ObjectChangeLogView(ConditionalLoginRequiredMixin, View):
base_template = None
tab = ViewTab(
label=_('Changelog'),
permission='extras.view_objectchange',
permission='core.view_objectchange',
weight=10000
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -22,7 +22,7 @@
"validate:formatting:scripts": "prettier -c src/**/*.ts"
},
"dependencies": {
"@fontsource-variable/plus-jakarta-sans": "^5.0.22",
"@fontsource-variable/plus-jakarta-sans": "^5.1.0",
"@mdi/font": "7.4.47",
"@tabler/core": "1.0.0-beta20",
"bootstrap": "5.3.3",
@ -31,7 +31,7 @@
"gridstack": "10.3.1",
"htmx.org": "1.9.12",
"query-string": "9.1.0",
"sass": "1.77.8",
"sass": "1.78.0",
"tom-select": "2.3.1",
"typeface-roboto-mono": "1.1.13"
},

View File

@ -200,10 +200,10 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.2.tgz#d8bae93ac8b815b2bd7a98078cf91e2724ef11e5"
integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==
"@fontsource-variable/plus-jakarta-sans@^5.0.22":
version "5.0.22"
resolved "https://registry.yarnpkg.com/@fontsource-variable/plus-jakarta-sans/-/plus-jakarta-sans-5.0.22.tgz#9f96b5f5381b93a422d35d89b0bc7871c4c06d60"
integrity sha512-YDV+FVn+icpvB004N3R973K/0W75FRPaEmeOoZ5nBuPAN8hizhUfTHpWpEv9FUr3C8FkPFwzrVl3gvdkzDdBxw==
"@fontsource-variable/plus-jakarta-sans@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@fontsource-variable/plus-jakarta-sans/-/plus-jakarta-sans-5.1.0.tgz#9dd5aa72277c43d408f6e34cd658d823fbef1fe1"
integrity sha512-K7o2GO7/quVFFyfjTqYZu0ng4KJGf60KSAGbvUWwKuH+/Giyl6Qe/EqnGLcjrl9AmXZzTvtRB1xpkASn8FNFoQ==
"@graphiql/plugin-explorer@3.2.2":
version "3.2.2"
@ -2561,7 +2561,16 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0"
is-regex "^1.1.4"
sass@1.77.8, sass@^1.71.1:
sass@1.78.0:
version "1.78.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.78.0.tgz#cef369b2f9dc21ea1d2cf22c979f52365da60841"
integrity sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
sass@^1.71.1:
version "1.77.8"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.8.tgz#9f18b449ea401759ef7ec1752a16373e296b52bd"
integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==

View File

@ -1,3 +1,3 @@
version: "4.1.0"
version: "4.1.1"
edition: "Community"
published: "2024-09-03"
published: "2024-09-12"

View File

@ -61,7 +61,7 @@
</div>
</div>
</div>
{% if perms.extras.view_objectchange %}
{% if perms.core.view_objectchange %}
<div class="row">
<div class="col-md-12">
<div class="card">

View File

@ -63,9 +63,9 @@
<td class="position-relative">
{% if object.latitude and object.longitude %}
{% if config.MAPS_URL %}
<div class="position-absolute top-50 end-0 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary">
<i class="mdi mdi-map-marker"></i> {% trans "Map It" %}
<div class="position-absolute top-50 end-0 me-2 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary btn-sm">
<i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a>
</div>
{% endif %}

View File

@ -95,9 +95,9 @@
<td class="position-relative">
{% if object.latitude and object.longitude %}
{% if config.MAPS_URL %}
<div class="position-absolute top-50 end-0 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary">
<i class="mdi mdi-map-marker"></i> {% trans "Map It" %}
<div class="position-absolute top-50 end-0 me-2 translate-middle-y d-print-none">
<a href="{{ config.MAPS_URL }}{{ object.latitude|unlocalize }},{{ object.longitude|unlocalize }}" target="_blank" class="btn btn-primary btn-sm">
<i class="mdi mdi-map-marker"></i> {% trans "Map" %}
</a>
</div>
{% endif %}

View File

@ -1,6 +1,6 @@
{% load i18n %}
{% if htmx_url and has_permission %}
<div class="htmx-container" hx-get="{{ htmx_url }}" hx-trigger="load" hx-target="this" hx-select="table" hx-swap="innerHTML"></div>
<div class="htmx-container" hx-get="{{ htmx_url }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"></div>
{% elif htmx_url %}
<div class="text-muted text-center">
<i class="mdi mdi-lock-outline"></i> {% trans "No permission to view this content" %}.

View File

@ -28,7 +28,7 @@
{% block page %}
{# Render the user's customized dashboard #}
<div class="grid-stack m-2" id="dashboard">
<div class="grid-stack m-2" id="dashboard" hx-disinherit="*">
{% for widget in dashboard %}
{% include 'extras/dashboard/widget.html' %}
{% endfor %}

View File

@ -71,7 +71,7 @@
</div>
</div>
</div>
{% if perms.extras.view_objectchange %}
{% if perms.core.view_objectchange %}
<div class="row">
<div class="col-md-12">
<div class="card">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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