Skip to content

Commit adad7c2

Browse files
committed
Merge branch 'main' into feature
2 parents 5ad6bd8 + 8cc6589 commit adad7c2

File tree

37 files changed

+1584
-1552
lines changed

37 files changed

+1584
-1552
lines changed

.github/ISSUE_TEMPLATE/01-feature_request.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ body:
1515
attributes:
1616
label: NetBox version
1717
description: What version of NetBox are you currently running?
18-
placeholder: v4.4.3
18+
placeholder: v4.4.4
1919
validations:
2020
required: true
2121
- type: dropdown

.github/ISSUE_TEMPLATE/02-bug_report.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ body:
2727
attributes:
2828
label: NetBox Version
2929
description: What version of NetBox are you currently running?
30-
placeholder: v4.4.3
30+
placeholder: v4.4.4
3131
validations:
3232
required: true
3333
- type: dropdown

base_requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ strawberry-graphql-django
166166
svgwrite
167167

168168
# Tabular dataset library (for table-based exports)
169-
# https://github.com/jazzband/tablib/blob/master/HISTORY.md
169+
# Current: https://github.com/jazzband/tablib/releases
170+
# Previous: https://github.com/jazzband/tablib/blob/master/HISTORY.md
170171
tablib
171172

172173
# Timezone data (required by django-timezone-field on Python 3.9+)

contrib/openapi.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"openapi": "3.0.3",
33
"info": {
44
"title": "NetBox REST API",
5-
"version": "4.4.3",
5+
"version": "4.4.4",
66
"license": {
77
"name": "Apache v2 License"
88
}
@@ -19678,14 +19678,14 @@
1967819678
"in": "query",
1967919679
"name": "object_type",
1968019680
"schema": {
19681-
"type": "integer"
19681+
"type": "string"
1968219682
}
1968319683
},
1968419684
{
1968519685
"in": "query",
1968619686
"name": "object_type__n",
1968719687
"schema": {
19688-
"type": "integer"
19688+
"type": "string"
1968919689
}
1969019690
},
1969119691
{
@@ -20507,14 +20507,14 @@
2050720507
"in": "query",
2050820508
"name": "related_object_type",
2050920509
"schema": {
20510-
"type": "integer"
20510+
"type": "string"
2051120511
}
2051220512
},
2051320513
{
2051420514
"in": "query",
2051520515
"name": "related_object_type__n",
2051620516
"schema": {
20517-
"type": "integer"
20517+
"type": "string"
2051820518
}
2051920519
},
2052020520
{
@@ -60413,14 +60413,14 @@
6041360413
"in": "query",
6041460414
"name": "assigned_object_type",
6041560415
"schema": {
60416-
"type": "integer"
60416+
"type": "string"
6041760417
}
6041860418
},
6041960419
{
6042060420
"in": "query",
6042160421
"name": "assigned_object_type__n",
6042260422
"schema": {
60423-
"type": "integer"
60423+
"type": "string"
6042460424
}
6042560425
},
6042660426
{
@@ -135594,14 +135594,14 @@
135594135594
"in": "query",
135595135595
"name": "assigned_object_type",
135596135596
"schema": {
135597-
"type": "integer"
135597+
"type": "string"
135598135598
}
135599135599
},
135600135600
{
135601135601
"in": "query",
135602135602
"name": "assigned_object_type__n",
135603135603
"schema": {
135604-
"type": "integer"
135604+
"type": "string"
135605135605
}
135606135606
},
135607135607
{
@@ -147446,14 +147446,14 @@
147446147446
"in": "query",
147447147447
"name": "parent_object_type",
147448147448
"schema": {
147449-
"type": "integer"
147449+
"type": "string"
147450147450
}
147451147451
},
147452147452
{
147453147453
"in": "query",
147454147454
"name": "parent_object_type__n",
147455147455
"schema": {
147456-
"type": "integer"
147456+
"type": "string"
147457147457
}
147458147458
},
147459147459
{

docs/release-notes/version-4.4.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# NetBox v4.4
22

3+
## v4.4.4 (2025-10-15)
4+
5+
### Bug Fixes
6+
7+
* [#20554](https://github.com/netbox-community/netbox/issues/20554) - Fix generic relation filters to accept `<app>.<model>` format matching POST requests
8+
* [#20574](https://github.com/netbox-community/netbox/issues/20574) - Fix excessive storage initialization overhead when listing scripts with remote backends
9+
* [#20584](https://github.com/netbox-community/netbox/issues/20584) - Enforce PoE mode requirement on interface templates when PoE type is set
10+
* [#20585](https://github.com/netbox-community/netbox/issues/20585) - Fix API schema generation crash for models with single-field UniqueConstraints
11+
* [#20587](https://github.com/netbox-community/netbox/issues/20587) - Fix upgrade.sh failure when removing stale content types
12+
13+
---
14+
315
## v4.4.3 (2025-10-14)
416

517
### Enhancements

netbox/core/filtersets.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class JobFilterSet(BaseFilterSet):
8080
method='search',
8181
label=_('Search'),
8282
)
83+
object_type = ContentTypeFilter()
8384
created = django_filters.DateTimeFilter()
8485
created__before = django_filters.DateTimeFilter(
8586
field_name='created',
@@ -169,6 +170,7 @@ class ObjectChangeFilterSet(BaseFilterSet):
169170
changed_object_type_id = django_filters.ModelMultipleChoiceFilter(
170171
queryset=ContentType.objects.all()
171172
)
173+
related_object_type = ContentTypeFilter()
172174
user_id = django_filters.ModelMultipleChoiceFilter(
173175
queryset=User.objects.all(),
174176
label=_('User (ID)'),

netbox/dcim/filtersets.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,7 @@ class Meta:
17641764

17651765
class MACAddressFilterSet(NetBoxModelFilterSet):
17661766
mac_address = MultiValueMACAddressFilter()
1767+
assigned_object_type = ContentTypeFilter()
17671768
device = MultiValueCharFilter(
17681769
method='filter_device',
17691770
field_name='name',

netbox/dcim/models/device_component_templates.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from dcim.choices import *
99
from dcim.constants import *
10+
from dcim.models.mixins import InterfaceValidationMixin
1011
from netbox.models import ChangeLoggedModel
1112
from utilities.fields import ColorField, NaturalOrderingField
1213
from utilities.mptt import TreeManager
@@ -405,7 +406,7 @@ def to_yaml(self):
405406
}
406407

407408

408-
class InterfaceTemplate(ModularComponentTemplateModel):
409+
class InterfaceTemplate(InterfaceValidationMixin, ModularComponentTemplateModel):
409410
"""
410411
A template for a physical data interface on a new Device.
411412
"""
@@ -469,8 +470,6 @@ def clean(self):
469470
super().clean()
470471

471472
if self.bridge:
472-
if self.pk and self.bridge_id == self.pk:
473-
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
474473
if self.device_type and self.device_type != self.bridge.device_type:
475474
raise ValidationError({
476475
'bridge': _(
@@ -484,11 +483,6 @@ def clean(self):
484483
).format(bridge=self.bridge)
485484
})
486485

487-
if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
488-
raise ValidationError({
489-
'rf_role': "Wireless role may be set only on wireless interfaces."
490-
})
491-
492486
def instantiate(self, **kwargs):
493487
return self.component_model(
494488
name=self.resolve_name(kwargs.get('module')),

netbox/dcim/models/device_components.py

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from dcim.choices import *
1212
from dcim.constants import *
1313
from dcim.fields import WWNField
14+
from dcim.models.mixins import InterfaceValidationMixin
1415
from netbox.choices import ColorChoices
1516
from netbox.models import OrganizationalModel, NetBoxModel
1617
from utilities.fields import ColorField, NaturalOrderingField
@@ -676,7 +677,14 @@ def mac_address(self):
676677
return self.primary_mac_address.mac_address
677678

678679

679-
class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEndpoint, TrackingModelMixin):
680+
class Interface(
681+
InterfaceValidationMixin,
682+
ModularComponentModel,
683+
BaseInterface,
684+
CabledObjectModel,
685+
PathEndpoint,
686+
TrackingModelMixin,
687+
):
680688
"""
681689
A network interface within a Device. A physical Interface can connect to exactly one other Interface.
682690
"""
@@ -893,10 +901,6 @@ def clean(self):
893901

894902
# Bridge validation
895903

896-
# An interface cannot be bridged to itself
897-
if self.pk and self.bridge_id == self.pk:
898-
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
899-
900904
# A bridged interface belongs to the same device or virtual chassis
901905
if self.bridge and self.bridge.device != self.device:
902906
if self.device.virtual_chassis is None:
@@ -942,29 +946,9 @@ def clean(self):
942946
)
943947
})
944948

945-
# PoE validation
946-
947-
# Only physical interfaces may have a PoE mode/type assigned
948-
if self.poe_mode and self.is_virtual:
949-
raise ValidationError({
950-
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
951-
})
952-
if self.poe_type and self.is_virtual:
953-
raise ValidationError({
954-
'poe_type': _("Virtual interfaces cannot have a PoE type.")
955-
})
956-
957-
# An interface with a PoE type set must also specify a mode
958-
if self.poe_type and not self.poe_mode:
959-
raise ValidationError({
960-
'poe_type': _("Must specify PoE mode when designating a PoE type.")
961-
})
962-
963949
# Wireless validation
964950

965-
# RF role & channel may only be set for wireless interfaces
966-
if self.rf_role and not self.is_wireless:
967-
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})
951+
# RF channel may only be set for wireless interfaces
968952
if self.rf_channel and not self.is_wireless:
969953
raise ValidationError({'rf_channel': _("Channel may be set only on wireless interfaces.")})
970954

netbox/dcim/models/mixins.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
from django.db import models
55
from django.utils.translation import gettext_lazy as _
66

7+
from dcim.constants import VIRTUAL_IFACE_TYPES, WIRELESS_IFACE_TYPES
8+
79
__all__ = (
810
'CachedScopeMixin',
11+
'InterfaceValidationMixin',
912
'RenderConfigMixin',
1013
)
1114

@@ -116,3 +119,33 @@ def cache_related_objects(self):
116119
self._site = self.scope.site
117120
self._location = self.scope
118121
cache_related_objects.alters_data = True
122+
123+
124+
class InterfaceValidationMixin:
125+
126+
def clean(self):
127+
super().clean()
128+
129+
# An interface cannot be bridged to itself
130+
if self.pk and self.bridge_id == self.pk:
131+
raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
132+
133+
# Only physical interfaces may have a PoE mode/type assigned
134+
if self.poe_mode and self.type in VIRTUAL_IFACE_TYPES:
135+
raise ValidationError({
136+
'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
137+
})
138+
if self.poe_type and self.type in VIRTUAL_IFACE_TYPES:
139+
raise ValidationError({
140+
'poe_type': _("Virtual interfaces cannot have a PoE type.")
141+
})
142+
143+
# An interface with a PoE type set must also specify a mode
144+
if self.poe_type and not self.poe_mode:
145+
raise ValidationError({
146+
'poe_type': _("Must specify PoE mode when designating a PoE type.")
147+
})
148+
149+
# RF role may be set only for wireless interfaces
150+
if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
151+
raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})

0 commit comments

Comments
 (0)