mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-06 01:17:16 +02:00
Compare commits
1 Commits
v4.5.7
...
21766-impr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
209c60ea6e |
@@ -15,7 +15,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v4.5.7
|
||||
placeholder: v4.5.6
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/02-bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/02-bug_report.yaml
vendored
@@ -27,7 +27,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox Version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v4.5.7
|
||||
placeholder: v4.5.6
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
2
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
@@ -8,7 +8,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox Version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v4.5.7
|
||||
placeholder: v4.5.6
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
@@ -47,7 +47,8 @@ django-rich
|
||||
|
||||
# Django integration for RQ (Reqis queuing)
|
||||
# https://github.com/rq/django-rq/blob/master/CHANGELOG.md
|
||||
django-rq
|
||||
# See https://github.com/netbox-community/netbox/issues/21696
|
||||
django-rq<4.0
|
||||
|
||||
# Provides a variety of storage backends
|
||||
# https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "NetBox REST API",
|
||||
"version": "4.5.7",
|
||||
"version": "4.5.6",
|
||||
"license": {
|
||||
"name": "Apache v2 License"
|
||||
}
|
||||
@@ -25468,7 +25468,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25488,7 +25488,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25501,7 +25501,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25514,7 +25514,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25527,7 +25527,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25540,7 +25540,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25553,7 +25553,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25566,7 +25566,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25579,7 +25579,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25592,7 +25592,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25605,7 +25605,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -25618,7 +25618,7 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
}
|
||||
},
|
||||
"explode": true,
|
||||
@@ -138591,50 +138591,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/extras/scripts/upload/": {
|
||||
"post": {
|
||||
"operationId": "extras_scripts_upload_create",
|
||||
"description": "Post a list of script module objects.",
|
||||
"tags": [
|
||||
"extras"
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ScriptModuleRequest"
|
||||
}
|
||||
},
|
||||
"multipart/form-data": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ScriptModuleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"tokenAuth": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ScriptModule"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/extras/subscriptions/": {
|
||||
"get": {
|
||||
"operationId": "extras_subscriptions_list",
|
||||
@@ -228090,14 +228046,13 @@
|
||||
"trunk-4c6p",
|
||||
"trunk-4c8p",
|
||||
"trunk-8c4p",
|
||||
"breakout-1c2p-2c1p",
|
||||
"breakout-1c4p-4c1p",
|
||||
"breakout-1c6p-6c1p",
|
||||
"breakout-2c4p-8c1p-shuffle"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c2p-2c1p` - 1C2P:2C1P breakout\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
@@ -228123,7 +228078,6 @@
|
||||
"4C6P trunk",
|
||||
"4C8P trunk",
|
||||
"8C4P trunk",
|
||||
"1C2P:2C1P breakout",
|
||||
"1C4P:4C1P breakout",
|
||||
"1C6P:6C1P breakout",
|
||||
"2C4P:8C1P breakout (shuffle)"
|
||||
@@ -228328,14 +228282,13 @@
|
||||
"trunk-4c6p",
|
||||
"trunk-4c8p",
|
||||
"trunk-8c4p",
|
||||
"breakout-1c2p-2c1p",
|
||||
"breakout-1c4p-4c1p",
|
||||
"breakout-1c6p-6c1p",
|
||||
"breakout-2c4p-8c1p-shuffle"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c2p-2c1p` - 1C2P:2C1P breakout\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
},
|
||||
"tenant": {
|
||||
"oneOf": [
|
||||
@@ -254535,7 +254488,8 @@
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0
|
||||
"minimum": 0,
|
||||
"title": "Size (MB)"
|
||||
},
|
||||
"owner": {
|
||||
"oneOf": [
|
||||
@@ -254820,15 +254774,14 @@
|
||||
"trunk-4c6p",
|
||||
"trunk-4c8p",
|
||||
"trunk-8c4p",
|
||||
"breakout-1c2p-2c1p",
|
||||
"breakout-1c4p-4c1p",
|
||||
"breakout-1c6p-6c1p",
|
||||
"breakout-2c4p-8c1p-shuffle",
|
||||
""
|
||||
],
|
||||
"type": "string",
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c2p-2c1p` - 1C2P:2C1P breakout\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
},
|
||||
"tenant": {
|
||||
"oneOf": [
|
||||
@@ -262866,13 +262819,15 @@
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Memory (MB)"
|
||||
},
|
||||
"disk": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Disk (MB)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
@@ -270385,56 +270340,6 @@
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"ScriptModule": {
|
||||
"type": "object",
|
||||
"description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"readOnly": true
|
||||
},
|
||||
"display": {
|
||||
"type": "string",
|
||||
"readOnly": true
|
||||
},
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"readOnly": true
|
||||
},
|
||||
"created": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"readOnly": true
|
||||
},
|
||||
"last_updated": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"readOnly": true,
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"created",
|
||||
"display",
|
||||
"file_path",
|
||||
"id",
|
||||
"last_updated"
|
||||
]
|
||||
},
|
||||
"ScriptModuleRequest": {
|
||||
"type": "object",
|
||||
"description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)",
|
||||
"properties": {
|
||||
"file": {
|
||||
"type": "string",
|
||||
"format": "binary",
|
||||
"writeOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file"
|
||||
]
|
||||
},
|
||||
"Service": {
|
||||
"type": "object",
|
||||
"description": "Base serializer class for models inheriting from PrimaryModel.",
|
||||
@@ -275479,7 +275384,8 @@
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0
|
||||
"minimum": 0,
|
||||
"title": "Size (MB)"
|
||||
},
|
||||
"owner": {
|
||||
"allOf": [
|
||||
@@ -275550,7 +275456,8 @@
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0
|
||||
"minimum": 0,
|
||||
"title": "Size (MB)"
|
||||
},
|
||||
"owner": {
|
||||
"oneOf": [
|
||||
@@ -275755,13 +275662,15 @@
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Memory (MB)"
|
||||
},
|
||||
"disk": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Disk (MB)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
@@ -276017,13 +275926,15 @@
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Memory (MB)"
|
||||
},
|
||||
"disk": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Disk (MB)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
@@ -277309,15 +277220,14 @@
|
||||
"trunk-4c6p",
|
||||
"trunk-4c8p",
|
||||
"trunk-8c4p",
|
||||
"breakout-1c2p-2c1p",
|
||||
"breakout-1c4p-4c1p",
|
||||
"breakout-1c6p-6c1p",
|
||||
"breakout-2c4p-8c1p-shuffle",
|
||||
""
|
||||
],
|
||||
"type": "string",
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c2p-2c1p` - 1C2P:2C1P breakout\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "f566e6df6572f5d0"
|
||||
"description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)",
|
||||
"x-spec-enum-id": "5e0f85310f0184ea"
|
||||
},
|
||||
"tenant": {
|
||||
"oneOf": [
|
||||
@@ -285610,13 +285520,15 @@
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Memory (MB)"
|
||||
},
|
||||
"disk": {
|
||||
"type": "integer",
|
||||
"maximum": 2147483647,
|
||||
"minimum": 0,
|
||||
"nullable": true
|
||||
"nullable": true,
|
||||
"title": "Disk (MB)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
|
||||
@@ -220,14 +220,6 @@ This parameter defines the URL of the repository that will be checked for new Ne
|
||||
|
||||
---
|
||||
|
||||
## RQ
|
||||
|
||||
Default: `{}` (Empty)
|
||||
|
||||
This is a wrapper for passing global configuration parameters to [Django RQ](https://github.com/rq/django-rq) to customize its behavior. It is employed within NetBox primarily to alter conditions during testing.
|
||||
|
||||
---
|
||||
|
||||
## RQ_DEFAULT_TIMEOUT
|
||||
|
||||
Default: `300`
|
||||
|
||||
@@ -1,31 +1,5 @@
|
||||
# NetBox v4.5
|
||||
|
||||
## v4.5.7 (2026-04-03)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#21095](https://github.com/netbox-community/netbox/issues/21095) - Adopt IEC unit labels (e.g. GiB) for virtual machine resources
|
||||
* [#21696](https://github.com/netbox-community/netbox/issues/21696) - Add support for django-rq 4.0 and introduce `RQ` configuration parameter
|
||||
* [#21701](https://github.com/netbox-community/netbox/issues/21701) - Support uploading custom scripts via the REST API (`/api/extras/scripts/upload/`)
|
||||
* [#21760](https://github.com/netbox-community/netbox/issues/21760) - Add a 1C2P:2C1P breakout cable profile
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* [#21655](https://github.com/netbox-community/netbox/issues/21655) - Optimize queries for object and multi-object type custom fields
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#20474](https://github.com/netbox-community/netbox/issues/20474) - Fix installation of modules with placeholder values in component names
|
||||
* [#21498](https://github.com/netbox-community/netbox/issues/21498) - Fix server error triggered by event rules referencing deleted objects
|
||||
* [#21533](https://github.com/netbox-community/netbox/issues/21533) - Ensure read-only fields are included in REST API responses upon object creation
|
||||
* [#21535](https://github.com/netbox-community/netbox/issues/21535) - Fix filtering of object-type custom fields when "is empty" is selected
|
||||
* [#21784](https://github.com/netbox-community/netbox/issues/21784) - Fix `AttributeError` exception when sorting a table as an anonymous user
|
||||
* [#21808](https://github.com/netbox-community/netbox/issues/21808) - Fix `RelatedObjectDoesNotExist` exception when viewing an interface with a virtual circuit termination
|
||||
* [#21810](https://github.com/netbox-community/netbox/issues/21810) - Fix `AttributeError` exception when viewing virtual chassis member
|
||||
* [#21825](https://github.com/netbox-community/netbox/issues/21825) - Fix sorting by broken columns in several object lists
|
||||
|
||||
---
|
||||
|
||||
## v4.5.6 (2026-03-31)
|
||||
|
||||
### Enhancements
|
||||
|
||||
@@ -1,48 +1,46 @@
|
||||
from django.test import RequestFactory, TestCase, tag
|
||||
|
||||
from circuits.models import CircuitGroupAssignment, CircuitTermination
|
||||
from circuits.tables import CircuitGroupAssignmentTable, CircuitTerminationTable
|
||||
from circuits.tables import *
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
|
||||
@tag('regression')
|
||||
class CircuitTerminationTableTest(TestCase):
|
||||
def test_every_orderable_field_does_not_throw_exception(self):
|
||||
terminations = CircuitTermination.objects.all()
|
||||
disallowed = {
|
||||
'actions',
|
||||
}
|
||||
|
||||
orderable_columns = [
|
||||
column.name
|
||||
for column in CircuitTerminationTable(terminations).columns
|
||||
if column.orderable and column.name not in disallowed
|
||||
]
|
||||
fake_request = RequestFactory().get('/')
|
||||
|
||||
for col in orderable_columns:
|
||||
for direction in ('-', ''):
|
||||
table = CircuitTerminationTable(terminations)
|
||||
table.order_by = f'{direction}{col}'
|
||||
table.as_html(fake_request)
|
||||
class CircuitTypeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CircuitTypeTable
|
||||
|
||||
|
||||
@tag('regression')
|
||||
class CircuitGroupAssignmentTableTest(TestCase):
|
||||
def test_every_orderable_field_does_not_throw_exception(self):
|
||||
assignment = CircuitGroupAssignment.objects.all()
|
||||
disallowed = {
|
||||
'actions',
|
||||
}
|
||||
class CircuitTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CircuitTable
|
||||
|
||||
orderable_columns = [
|
||||
column.name
|
||||
for column in CircuitGroupAssignmentTable(assignment).columns
|
||||
if column.orderable and column.name not in disallowed
|
||||
]
|
||||
fake_request = RequestFactory().get('/')
|
||||
|
||||
for col in orderable_columns:
|
||||
for direction in ('-', ''):
|
||||
table = CircuitGroupAssignmentTable(assignment)
|
||||
table.order_by = f'{direction}{col}'
|
||||
table.as_html(fake_request)
|
||||
class CircuitTerminationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CircuitTerminationTable
|
||||
|
||||
|
||||
class CircuitGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CircuitGroupTable
|
||||
|
||||
|
||||
class CircuitGroupAssignmentTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CircuitGroupAssignmentTable
|
||||
|
||||
|
||||
class ProviderTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ProviderTable
|
||||
|
||||
|
||||
class ProviderAccountTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ProviderAccountTable
|
||||
|
||||
|
||||
class ProviderNetworkTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ProviderNetworkTable
|
||||
|
||||
|
||||
class VirtualCircuitTypeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualCircuitTypeTable
|
||||
|
||||
|
||||
class VirtualCircuitTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualCircuitTable
|
||||
|
||||
|
||||
class VirtualCircuitTerminationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualCircuitTerminationTable
|
||||
|
||||
26
netbox/core/tests/test_tables.py
Normal file
26
netbox/core/tests/test_tables.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from core.models import ObjectChange
|
||||
from core.tables import *
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
|
||||
class DataSourceTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = DataSourceTable
|
||||
|
||||
|
||||
class DataFileTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = DataFileTable
|
||||
|
||||
|
||||
class JobTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = JobTable
|
||||
|
||||
|
||||
class ObjectChangeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ObjectChangeTable
|
||||
queryset_sources = [
|
||||
('ObjectChangeListView', ObjectChange.objects.valid_models()),
|
||||
]
|
||||
|
||||
|
||||
class ConfigRevisionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConfigRevisionTable
|
||||
204
netbox/dcim/tests/test_tables.py
Normal file
204
netbox/dcim/tests/test_tables.py
Normal file
@@ -0,0 +1,204 @@
|
||||
from dcim.models import ConsolePort, Interface, PowerPort
|
||||
from dcim.tables import *
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
#
|
||||
# Sites
|
||||
#
|
||||
|
||||
|
||||
class RegionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RegionTable
|
||||
|
||||
|
||||
class SiteGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = SiteGroupTable
|
||||
|
||||
|
||||
class SiteTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = SiteTable
|
||||
|
||||
|
||||
class LocationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = LocationTable
|
||||
|
||||
|
||||
#
|
||||
# Racks
|
||||
#
|
||||
|
||||
class RackRoleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RackRoleTable
|
||||
|
||||
|
||||
class RackTypeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RackTypeTable
|
||||
|
||||
|
||||
class RackTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RackTable
|
||||
|
||||
|
||||
class RackReservationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RackReservationTable
|
||||
|
||||
|
||||
#
|
||||
# Device types
|
||||
#
|
||||
|
||||
class ManufacturerTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ManufacturerTable
|
||||
|
||||
|
||||
class DeviceTypeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = DeviceTypeTable
|
||||
|
||||
|
||||
#
|
||||
# Module types
|
||||
#
|
||||
|
||||
class ModuleTypeProfileTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ModuleTypeProfileTable
|
||||
|
||||
|
||||
class ModuleTypeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ModuleTypeTable
|
||||
|
||||
|
||||
class ModuleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ModuleTable
|
||||
|
||||
|
||||
#
|
||||
# Devices
|
||||
#
|
||||
|
||||
class DeviceRoleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = DeviceRoleTable
|
||||
|
||||
|
||||
class PlatformTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PlatformTable
|
||||
|
||||
|
||||
class DeviceTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = DeviceTable
|
||||
|
||||
|
||||
#
|
||||
# Device components
|
||||
#
|
||||
|
||||
class ConsolePortTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConsolePortTable
|
||||
|
||||
|
||||
class ConsoleServerPortTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConsoleServerPortTable
|
||||
|
||||
|
||||
class PowerPortTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PowerPortTable
|
||||
|
||||
|
||||
class PowerOutletTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PowerOutletTable
|
||||
|
||||
|
||||
class InterfaceTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = InterfaceTable
|
||||
|
||||
|
||||
class FrontPortTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = FrontPortTable
|
||||
|
||||
|
||||
class RearPortTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RearPortTable
|
||||
|
||||
|
||||
class ModuleBayTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ModuleBayTable
|
||||
|
||||
|
||||
class DeviceBayTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = DeviceBayTable
|
||||
|
||||
|
||||
class InventoryItemTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = InventoryItemTable
|
||||
|
||||
|
||||
class InventoryItemRoleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = InventoryItemRoleTable
|
||||
|
||||
|
||||
#
|
||||
# Connections
|
||||
#
|
||||
|
||||
class ConsoleConnectionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConsoleConnectionTable
|
||||
queryset_sources = [
|
||||
('ConsoleConnectionsListView', ConsolePort.objects.filter(_path__is_complete=True)),
|
||||
]
|
||||
|
||||
|
||||
class PowerConnectionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PowerConnectionTable
|
||||
queryset_sources = [
|
||||
('PowerConnectionsListView', PowerPort.objects.filter(_path__is_complete=True)),
|
||||
]
|
||||
|
||||
|
||||
class InterfaceConnectionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = InterfaceConnectionTable
|
||||
queryset_sources = [
|
||||
('InterfaceConnectionsListView', Interface.objects.filter(_path__is_complete=True)),
|
||||
]
|
||||
|
||||
|
||||
#
|
||||
# Cables
|
||||
#
|
||||
|
||||
class CableTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CableTable
|
||||
|
||||
|
||||
#
|
||||
# Power
|
||||
#
|
||||
|
||||
class PowerPanelTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PowerPanelTable
|
||||
|
||||
|
||||
class PowerFeedTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PowerFeedTable
|
||||
|
||||
|
||||
#
|
||||
# Virtual chassis
|
||||
#
|
||||
|
||||
class VirtualChassisTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualChassisTable
|
||||
|
||||
|
||||
#
|
||||
# Virtual device contexts
|
||||
#
|
||||
|
||||
class VirtualDeviceContextTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualDeviceContextTable
|
||||
|
||||
|
||||
#
|
||||
# MAC addresses
|
||||
#
|
||||
|
||||
class MACAddressTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = MACAddressTable
|
||||
@@ -1,24 +1,84 @@
|
||||
from django.test import RequestFactory, TestCase, tag
|
||||
|
||||
from extras.models import EventRule
|
||||
from extras.tables import EventRuleTable
|
||||
from extras.models import Bookmark, Notification, Subscription
|
||||
from extras.tables import *
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
|
||||
@tag('regression')
|
||||
class EventRuleTableTest(TestCase):
|
||||
def test_every_orderable_field_does_not_throw_exception(self):
|
||||
rule = EventRule.objects.all()
|
||||
disallowed = {
|
||||
'actions',
|
||||
}
|
||||
class CustomFieldTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CustomFieldTable
|
||||
|
||||
orderable_columns = [
|
||||
column.name for column in EventRuleTable(rule).columns if column.orderable and column.name not in disallowed
|
||||
]
|
||||
fake_request = RequestFactory().get('/')
|
||||
|
||||
for col in orderable_columns:
|
||||
for direction in ('-', ''):
|
||||
table = EventRuleTable(rule)
|
||||
table.order_by = f'{direction}{col}'
|
||||
table.as_html(fake_request)
|
||||
class CustomFieldChoiceSetTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CustomFieldChoiceSetTable
|
||||
|
||||
|
||||
class CustomLinkTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = CustomLinkTable
|
||||
|
||||
|
||||
class ExportTemplateTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ExportTemplateTable
|
||||
|
||||
|
||||
class SavedFilterTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = SavedFilterTable
|
||||
|
||||
|
||||
class TableConfigTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TableConfigTable
|
||||
|
||||
|
||||
class BookmarkTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = BookmarkTable
|
||||
queryset_sources = [
|
||||
('BookmarkListView', Bookmark.objects.all()),
|
||||
]
|
||||
|
||||
|
||||
class NotificationGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = NotificationGroupTable
|
||||
|
||||
|
||||
class NotificationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = NotificationTable
|
||||
queryset_sources = [
|
||||
('NotificationListView', Notification.objects.all()),
|
||||
]
|
||||
|
||||
|
||||
class SubscriptionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = SubscriptionTable
|
||||
queryset_sources = [
|
||||
('SubscriptionListView', Subscription.objects.all()),
|
||||
]
|
||||
|
||||
|
||||
class WebhookTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = WebhookTable
|
||||
|
||||
|
||||
class EventRuleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = EventRuleTable
|
||||
|
||||
|
||||
class TagTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TagTable
|
||||
|
||||
|
||||
class ConfigContextProfileTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConfigContextProfileTable
|
||||
|
||||
|
||||
class ConfigContextTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConfigContextTable
|
||||
|
||||
|
||||
class ConfigTemplateTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ConfigTemplateTable
|
||||
|
||||
|
||||
class ImageAttachmentTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ImageAttachmentTable
|
||||
|
||||
|
||||
class JournalEntryTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = JournalEntryTable
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from django.test import RequestFactory, TestCase
|
||||
from netaddr import IPNetwork
|
||||
|
||||
from ipam.models import IPAddress, IPRange, Prefix
|
||||
from ipam.tables import AnnotatedIPAddressTable
|
||||
from ipam.models import FHRPGroupAssignment, IPAddress, IPRange, Prefix
|
||||
from ipam.tables import *
|
||||
from ipam.utils import annotate_ip_space
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
|
||||
class AnnotatedIPAddressTableTest(TestCase):
|
||||
@@ -168,3 +169,82 @@ class AnnotatedIPAddressTableTest(TestCase):
|
||||
# Pools are fully usable
|
||||
self.assertEqual(available.first_ip, '2001:db8:1::/126')
|
||||
self.assertEqual(available.size, 4)
|
||||
|
||||
|
||||
#
|
||||
# Table ordering tests
|
||||
#
|
||||
|
||||
class VRFTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VRFTable
|
||||
|
||||
|
||||
class RouteTargetTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RouteTargetTable
|
||||
|
||||
|
||||
class RIRTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RIRTable
|
||||
|
||||
|
||||
class AggregateTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = AggregateTable
|
||||
|
||||
|
||||
class RoleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = RoleTable
|
||||
|
||||
|
||||
class PrefixTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = PrefixTable
|
||||
|
||||
|
||||
class IPRangeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IPRangeTable
|
||||
|
||||
|
||||
class IPAddressTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IPAddressTable
|
||||
|
||||
|
||||
class FHRPGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = FHRPGroupTable
|
||||
|
||||
|
||||
class FHRPGroupAssignmentTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = FHRPGroupAssignmentTable
|
||||
queryset_sources = [
|
||||
('FHRPGroupAssignmentTable', FHRPGroupAssignment.objects.all()),
|
||||
]
|
||||
|
||||
|
||||
class VLANGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VLANGroupTable
|
||||
|
||||
|
||||
class VLANTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VLANTable
|
||||
|
||||
|
||||
class VLANTranslationPolicyTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VLANTranslationPolicyTable
|
||||
|
||||
|
||||
class VLANTranslationRuleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VLANTranslationRuleTable
|
||||
|
||||
|
||||
class ASNRangeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ASNRangeTable
|
||||
|
||||
|
||||
class ASNTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ASNTable
|
||||
|
||||
|
||||
class ServiceTemplateTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ServiceTemplateTable
|
||||
|
||||
|
||||
class ServiceTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ServiceTable
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version: "4.5.7"
|
||||
version: "4.5.6"
|
||||
edition: "Community"
|
||||
published: "2026-04-03"
|
||||
published: "2026-03-31"
|
||||
|
||||
26
netbox/tenancy/tests/test_tables.py
Normal file
26
netbox/tenancy/tests/test_tables.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from tenancy.tables import *
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
|
||||
class TenantGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TenantGroupTable
|
||||
|
||||
|
||||
class TenantTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TenantTable
|
||||
|
||||
|
||||
class ContactGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ContactGroupTable
|
||||
|
||||
|
||||
class ContactRoleTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ContactRoleTable
|
||||
|
||||
|
||||
class ContactTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ContactTable
|
||||
|
||||
|
||||
class ContactAssignmentTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ContactAssignmentTable
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,24 +1,26 @@
|
||||
from django.test import RequestFactory, TestCase, tag
|
||||
|
||||
from users.models import Token
|
||||
from users.tables import TokenTable
|
||||
from users.tables import *
|
||||
from utilities.testing import TableTestCases
|
||||
|
||||
|
||||
class TokenTableTest(TestCase):
|
||||
@tag('regression')
|
||||
def test_every_orderable_field_does_not_throw_exception(self):
|
||||
tokens = Token.objects.all()
|
||||
disallowed = {'actions'}
|
||||
class TokenTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TokenTable
|
||||
|
||||
orderable_columns = [
|
||||
column.name for column in TokenTable(tokens).columns
|
||||
if column.orderable and column.name not in disallowed
|
||||
]
|
||||
fake_request = RequestFactory().get("/")
|
||||
|
||||
for col in orderable_columns:
|
||||
for direction in ('-', ''):
|
||||
with self.subTest(col=col, direction=direction):
|
||||
table = TokenTable(tokens)
|
||||
table.order_by = f'{direction}{col}'
|
||||
table.as_html(fake_request)
|
||||
class UserTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = UserTable
|
||||
|
||||
|
||||
class GroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = GroupTable
|
||||
|
||||
|
||||
class ObjectPermissionTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ObjectPermissionTable
|
||||
|
||||
|
||||
class OwnerGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = OwnerGroupTable
|
||||
|
||||
|
||||
class OwnerTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = OwnerTable
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from .api import *
|
||||
from .base import *
|
||||
from .filtersets import *
|
||||
from .tables import *
|
||||
from .utils import *
|
||||
from .views import *
|
||||
|
||||
130
netbox/utilities/testing/tables.py
Normal file
130
netbox/utilities/testing/tables.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import inspect
|
||||
from importlib import import_module
|
||||
|
||||
from django.test import RequestFactory
|
||||
|
||||
from netbox.views import generic
|
||||
|
||||
from .base import TestCase
|
||||
|
||||
__all__ = (
|
||||
"ModelTableTestCase",
|
||||
"TableTestCases",
|
||||
)
|
||||
|
||||
|
||||
class ModelTableTestCase(TestCase):
|
||||
"""
|
||||
Shared helpers for model-backed table tests.
|
||||
|
||||
Concrete subclasses should set `table` and may override `get_queryset()`
|
||||
or `excluded_orderable_columns` as needed.
|
||||
"""
|
||||
table = None
|
||||
excluded_orderable_columns = frozenset({"actions"})
|
||||
|
||||
# Optional explicit override for odd cases
|
||||
queryset_sources = None
|
||||
|
||||
# Only these view types are considered sortable queryset sources by default
|
||||
queryset_source_view_classes = (generic.ObjectListView,)
|
||||
|
||||
@classmethod
|
||||
def validate_table_test_case(cls):
|
||||
if cls.table is None:
|
||||
raise AssertionError(f"{cls.__name__} must define `table`")
|
||||
if getattr(cls.table._meta, "model", None) is None:
|
||||
raise AssertionError(f"{cls.__name__}.table must be model-backed")
|
||||
|
||||
def get_request(self):
|
||||
request = RequestFactory().get("/")
|
||||
request.user = self.user
|
||||
return request
|
||||
|
||||
def get_table(self, queryset):
|
||||
return self.table(queryset)
|
||||
|
||||
@classmethod
|
||||
def is_queryset_source_view(cls, view):
|
||||
model = cls.table._meta.model
|
||||
app_label = model._meta.app_label
|
||||
|
||||
return (
|
||||
inspect.isclass(view)
|
||||
and view.__module__.startswith(f"{app_label}.views")
|
||||
and getattr(view, "table", None) is cls.table
|
||||
and getattr(view, "queryset", None) is not None
|
||||
and issubclass(view, cls.queryset_source_view_classes)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_queryset_sources(cls):
|
||||
"""
|
||||
Return iterable of (label, queryset) pairs to test.
|
||||
|
||||
By default, only discover list-style views that declare this table.
|
||||
That keeps bulk edit/delete confirmation tables out of the ordering
|
||||
smoke test.
|
||||
"""
|
||||
if cls.queryset_sources is not None:
|
||||
return tuple(cls.queryset_sources)
|
||||
|
||||
model = cls.table._meta.model
|
||||
app_label = model._meta.app_label
|
||||
module = import_module(f"{app_label}.views")
|
||||
|
||||
sources = []
|
||||
for _, view in inspect.getmembers(module, inspect.isclass):
|
||||
if not cls.is_queryset_source_view(view):
|
||||
continue
|
||||
|
||||
queryset = view.queryset
|
||||
if hasattr(queryset, "all"):
|
||||
queryset = queryset.all()
|
||||
|
||||
sources.append((view.__name__, queryset))
|
||||
|
||||
if not sources:
|
||||
raise AssertionError(
|
||||
f"{cls.__name__} could not find any list-style queryset source for "
|
||||
f"{cls.table.__module__}.{cls.table.__name__}; "
|
||||
"set `queryset_sources` explicitly if needed."
|
||||
)
|
||||
|
||||
return tuple(sources)
|
||||
|
||||
def iter_orderable_columns(self, queryset):
|
||||
for column in self.get_table(queryset).columns:
|
||||
if not column.orderable:
|
||||
continue
|
||||
if column.name in self.excluded_orderable_columns:
|
||||
continue
|
||||
yield column.name
|
||||
|
||||
|
||||
class TableTestCases:
|
||||
"""
|
||||
Keep test_* methods nested to avoid unittest auto-discovering the reusable
|
||||
base classes directly.
|
||||
"""
|
||||
|
||||
class OrderableColumnsTestCase(ModelTableTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.validate_table_test_case()
|
||||
|
||||
def test_every_orderable_column_renders(self):
|
||||
request = self.get_request()
|
||||
|
||||
for source_name, queryset in self.get_queryset_sources():
|
||||
for column_name in self.iter_orderable_columns(queryset):
|
||||
for direction, prefix in (("asc", ""), ("desc", "-")):
|
||||
with self.cleanupSubTest(
|
||||
source=source_name,
|
||||
column=column_name,
|
||||
direction=direction,
|
||||
):
|
||||
table = self.get_table(queryset)
|
||||
table.order_by = f"{prefix}{column_name}"
|
||||
table.as_html(request)
|
||||
26
netbox/virtualization/tests/test_tables.py
Normal file
26
netbox/virtualization/tests/test_tables.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from utilities.testing import TableTestCases
|
||||
from virtualization.tables import *
|
||||
|
||||
|
||||
class ClusterTypeTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ClusterTypeTable
|
||||
|
||||
|
||||
class ClusterGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ClusterGroupTable
|
||||
|
||||
|
||||
class ClusterTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = ClusterTable
|
||||
|
||||
|
||||
class VirtualMachineTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualMachineTable
|
||||
|
||||
|
||||
class VMInterfaceTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VMInterfaceTable
|
||||
|
||||
|
||||
class VirtualDiskTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = VirtualDiskTable
|
||||
@@ -1,23 +1,42 @@
|
||||
from django.test import RequestFactory, TestCase, tag
|
||||
|
||||
from vpn.models import TunnelTermination
|
||||
from vpn.tables import TunnelTerminationTable
|
||||
from utilities.testing import TableTestCases
|
||||
from vpn.tables import *
|
||||
|
||||
|
||||
@tag('regression')
|
||||
class TunnelTerminationTableTest(TestCase):
|
||||
def test_every_orderable_field_does_not_throw_exception(self):
|
||||
terminations = TunnelTermination.objects.all()
|
||||
fake_request = RequestFactory().get("/")
|
||||
disallowed = {'actions'}
|
||||
class TunnelGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TunnelGroupTable
|
||||
|
||||
orderable_columns = [
|
||||
column.name for column in TunnelTerminationTable(terminations).columns
|
||||
if column.orderable and column.name not in disallowed
|
||||
]
|
||||
|
||||
for col in orderable_columns:
|
||||
for dir in ('-', ''):
|
||||
table = TunnelTerminationTable(terminations)
|
||||
table.order_by = f'{dir}{col}'
|
||||
table.as_html(fake_request)
|
||||
class TunnelTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TunnelTable
|
||||
|
||||
|
||||
class TunnelTerminationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = TunnelTerminationTable
|
||||
|
||||
|
||||
class IKEProposalTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IKEProposalTable
|
||||
|
||||
|
||||
class IKEPolicyTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IKEPolicyTable
|
||||
|
||||
|
||||
class IPSecProposalTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IPSecProposalTable
|
||||
|
||||
|
||||
class IPSecPolicyTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IPSecPolicyTable
|
||||
|
||||
|
||||
class IPSecProfileTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = IPSecProfileTable
|
||||
|
||||
|
||||
class L2VPNTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = L2VPNTable
|
||||
|
||||
|
||||
class L2VPNTerminationTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = L2VPNTerminationTable
|
||||
|
||||
14
netbox/wireless/tests/test_tables.py
Normal file
14
netbox/wireless/tests/test_tables.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from utilities.testing import TableTestCases
|
||||
from wireless.tables import *
|
||||
|
||||
|
||||
class WirelessLANGroupTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = WirelessLANGroupTable
|
||||
|
||||
|
||||
class WirelessLANTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = WirelessLANTable
|
||||
|
||||
|
||||
class WirelessLinkTableTest(TableTestCases.OrderableColumnsTestCase):
|
||||
table = WirelessLinkTable
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[project]
|
||||
name = "netbox"
|
||||
version = "4.5.7"
|
||||
version = "4.5.6"
|
||||
requires-python = ">=3.12"
|
||||
description = "The premier source of truth powering network automation."
|
||||
readme = "README.md"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
colorama==0.4.6
|
||||
Django==5.2.12
|
||||
django-cors-headers==4.9.0
|
||||
django-debug-toolbar==6.3.0
|
||||
django-debug-toolbar==6.2.0
|
||||
django-filter==25.2
|
||||
django-graphiql-debug-toolbar==0.2.0
|
||||
django-htmx==1.27.0
|
||||
@@ -17,7 +17,7 @@ django-taggit==6.1.0
|
||||
django-timezone-field==7.2.1
|
||||
djangorestframework==3.16.1
|
||||
drf-spectacular==0.29.0
|
||||
drf-spectacular-sidecar==2026.4.1
|
||||
drf-spectacular-sidecar==2026.3.1
|
||||
feedparser==6.0.12
|
||||
gunicorn==25.3.0
|
||||
Jinja2==3.1.6
|
||||
@@ -29,7 +29,7 @@ mkdocstrings==1.0.3
|
||||
mkdocstrings-python==2.0.3
|
||||
netaddr==1.3.0
|
||||
nh3==0.3.4
|
||||
Pillow==12.2.0
|
||||
Pillow==12.1.1
|
||||
psycopg[c,pool]==3.3.3
|
||||
PyYAML==6.0.3
|
||||
requests==2.33.1
|
||||
@@ -41,4 +41,4 @@ strawberry-graphql==0.312.2
|
||||
strawberry-graphql-django==0.82.1
|
||||
svgwrite==1.4.3
|
||||
tablib==3.9.0
|
||||
tzdata==2026.1
|
||||
tzdata==2025.3
|
||||
|
||||
Reference in New Issue
Block a user