diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5f0c9fa66..87213a74b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -92,7 +92,7 @@ jobs:
pip install coverage tblib
- name: Build documentation
- run: mkdocs build
+ run: zensical build
- name: Collect static files
run: python netbox/manage.py collectstatic --no-input
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 361dfe72f..79b680f6b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -21,11 +21,11 @@ repos:
language: system
pass_filenames: false
types: [python]
- - id: mkdocs-build
+ - id: zensical-build
name: "Build documentation"
- description: "Build the documentation with mkdocs"
+ description: "Build the documentation with Zensical"
files: 'docs/'
- entry: mkdocs build
+ entry: zensical build
language: system
pass_filenames: false
- id: yarn-validate
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index c69c51748..5e6dda025 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -1,10 +1,10 @@
version: 2
build:
- os: ubuntu-22.04
+ os: ubuntu-24.04
tools:
python: "3.12"
-mkdocs:
- configuration: mkdocs.yml
-python:
- install:
- - requirements: requirements.txt
+ commands:
+ - pip install -r requirements.txt
+ - python -m zensical build --config-file mkdocs.yml
+ - mkdir -p $READTHEDOCS_OUTPUT/html/
+ - cp -r netbox/project-static/docs/* $READTHEDOCS_OUTPUT/html/
diff --git a/base_requirements.txt b/base_requirements.txt
index eb1ecd84b..d88174c5e 100644
--- a/base_requirements.txt
+++ b/base_requirements.txt
@@ -4,7 +4,7 @@ colorama
# The Python web framework on which NetBox is built
# https://docs.djangoproject.com/en/stable/releases/
-Django==5.2.*
+Django==6.0.*
# Django middleware which permits cross-domain API requests
# https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst
@@ -35,7 +35,9 @@ django-pglocks
# Prometheus metrics library for Django
# https://github.com/korfuri/django-prometheus/blob/master/CHANGELOG.md
-django-prometheus
+# TODO: 2.4.1 is incompatible with Django>=6.0, but a fixed release is expected
+# https://github.com/django-commons/django-prometheus/issues/494
+django-prometheus>=2.4.0,<2.5.0,!=2.4.1
# Django caching backend using Redis
# https://github.com/jazzband/django-redis/blob/master/CHANGELOG.rst
@@ -69,7 +71,7 @@ django-timezone-field
# A REST API framework for Django projects
# https://www.django-rest-framework.org/community/release-notes/
# TODO: Re-evaluate the monkey-patch of get_unique_validators() before upgrading
-djangorestframework==3.16.1
+djangorestframework==3.17.1
# Sane and flexible OpenAPI 3 schema generation for Django REST framework.
# https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst
@@ -99,10 +101,6 @@ jsonschema
# https://python-markdown.github.io/changelog/
Markdown
-# MkDocs
-# https://github.com/mkdocs/mkdocs/releases
-mkdocs<2.0
-
# MkDocs Material theme (for documentation build)
# https://squidfunk.github.io/mkdocs-material/changelog/
mkdocs-material
@@ -177,3 +175,7 @@ tablib
# Timezone data (required by django-timezone-field on Python 3.9+)
# https://github.com/python/tzdata/blob/master/NEWS.md
tzdata
+
+# Documentation builder (succeeds mkdocs)
+# https://github.com/zensical/zensical
+zensical
diff --git a/contrib/openapi.json b/contrib/openapi.json
index 5b4c99a0a..57bfe97bd 100644
--- a/contrib/openapi.json
+++ b/contrib/openapi.json
@@ -2,7 +2,7 @@
"openapi": "3.0.3",
"info": {
"title": "NetBox REST API",
- "version": "4.5.10",
+ "version": "4.6.0-beta2",
"license": {
"name": "Apache v2 License"
}
@@ -733,6 +733,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -2103,6 +2112,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -3969,6 +3987,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -6007,6 +6034,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -7999,6 +8035,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -9930,6 +9975,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -11327,6 +11381,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -12954,6 +13017,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -14212,6 +14284,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -15774,6 +15855,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -17100,6 +17190,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -19067,6 +19166,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -20115,6 +20223,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -21770,6 +21887,15 @@
"format": "date-time"
}
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "started",
@@ -22720,6 +22846,15 @@
"format": "uuid"
}
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "time_after",
@@ -23447,6 +23582,15 @@
"type": "string"
},
"description": "Search"
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
}
],
"tags": [
@@ -23514,6 +23658,1209 @@
}
}
},
+ "/api/dcim/cable-bundles/": {
+ "get": {
+ "operationId": "dcim_cable_bundles_list",
+ "description": "Get a list of cable bundle objects.",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "created",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__empty",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "id__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__empty",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Number of results to return per page.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "modified_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "offset",
+ "required": false,
+ "in": "query",
+ "description": "The initial index from which to return the results.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "ordering",
+ "required": false,
+ "in": "query",
+ "description": "Which field to use when ordering the results.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "q",
+ "schema": {
+ "type": "string"
+ },
+ "description": "Search"
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "tag",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "updated_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaginatedCableBundleList"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "post": {
+ "operationId": "dcim_cable_bundles_create",
+ "description": "Post a list of cable bundle objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ ]
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "201": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "dcim_cable_bundles_bulk_update",
+ "description": "Put a list of cable bundle objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "dcim_cable_bundles_bulk_partial_update",
+ "description": "Patch a list of cable bundle objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "dcim_cable_bundles_bulk_destroy",
+ "description": "Delete a list of cable bundle objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
+ "/api/dcim/cable-bundles/{id}/": {
+ "get": {
+ "operationId": "dcim_cable_bundles_retrieve",
+ "description": "Get a cable bundle object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this cable bundle.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "dcim_cable_bundles_update",
+ "description": "Put a cable bundle object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this cable bundle.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/CableBundleRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "dcim_cable_bundles_partial_update",
+ "description": "Patch a cable bundle object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this cable bundle.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedCableBundleRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedCableBundleRequest"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "dcim_cable_bundles_destroy",
+ "description": "Delete a cable bundle object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this cable bundle.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
"/api/dcim/cable-terminations/": {
"get": {
"operationId": "dcim_cable_terminations_list",
@@ -24000,6 +25347,15 @@
"type": "string"
}
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "termination_id",
@@ -24188,6 +25544,60 @@
"operationId": "dcim_cables_list",
"description": "Get a list of cable objects.",
"parameters": [
+ {
+ "in": "query",
+ "name": "bundle",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Cable bundle (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "bundle__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Cable bundle (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "bundle_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Cable bundle (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "bundle_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Cable bundle (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "circuittermination_id",
@@ -25695,6 +27105,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -27562,6 +28981,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -30089,6 +31517,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -31715,6 +33152,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -34242,6 +35688,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -35317,6 +36772,13 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "enabled",
+ "schema": {
+ "type": "boolean"
+ }
+ },
{
"in": "query",
"name": "id",
@@ -35838,6 +37300,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -36829,6 +38300,13 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "enabled",
+ "schema": {
+ "type": "boolean"
+ }
+ },
{
"in": "query",
"name": "id",
@@ -37764,6 +39242,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -39494,6 +40981,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -42307,6 +43803,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "subdevice_role",
@@ -46792,6 +48297,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -48889,6 +50403,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -51503,6 +53026,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -53680,6 +55212,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -57828,6 +59369,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -60224,6 +61774,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -61891,6 +63450,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -64472,6 +66040,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -66561,6 +68138,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -68215,6 +69801,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -69713,6 +71308,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -70459,6 +72063,13 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "enabled",
+ "schema": {
+ "type": "boolean"
+ }
+ },
{
"in": "query",
"name": "id",
@@ -71159,6 +72770,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -72150,6 +73770,13 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "enabled",
+ "schema": {
+ "type": "boolean"
+ }
+ },
{
"in": "query",
"name": "id",
@@ -73262,6 +74889,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -74560,6 +76196,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -76309,6 +77954,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -78386,6 +80040,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -80083,6 +81746,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -82185,6 +83857,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -84546,6 +86227,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -87285,6 +88975,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -89221,6 +90920,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -90686,6 +92394,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -93229,6 +94946,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -94023,6 +95749,1360 @@
}
}
},
+ "/api/dcim/rack-groups/": {
+ "get": {
+ "operationId": "dcim_rack_groups_list",
+ "description": "Get a list of rack group objects.",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "created",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__empty",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "id__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__empty",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Number of results to return per page.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "modified_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "offset",
+ "required": false,
+ "in": "query",
+ "description": "The initial index from which to return the results.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "ordering",
+ "required": false,
+ "in": "query",
+ "description": "Which field to use when ordering the results.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "q",
+ "schema": {
+ "type": "string"
+ },
+ "description": "Search"
+ },
+ {
+ "in": "query",
+ "name": "slug",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "slug__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "tag",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "updated_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaginatedRackGroupList"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "post": {
+ "operationId": "dcim_rack_groups_create",
+ "description": "Post a list of rack group objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ ]
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "201": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "dcim_rack_groups_bulk_update",
+ "description": "Put a list of rack group objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "dcim_rack_groups_bulk_partial_update",
+ "description": "Patch a list of rack group objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "dcim_rack_groups_bulk_destroy",
+ "description": "Delete a list of rack group objects.",
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
+ "/api/dcim/rack-groups/{id}/": {
+ "get": {
+ "operationId": "dcim_rack_groups_retrieve",
+ "description": "Get a rack group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this rack group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "dcim_rack_groups_update",
+ "description": "Put a rack group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this rack group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/RackGroupRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "dcim_rack_groups_partial_update",
+ "description": "Patch a rack group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this rack group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedRackGroupRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedRackGroupRequest"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "dcim_rack_groups_destroy",
+ "description": "Delete a rack group object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this rack group.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "dcim"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
"/api/dcim/rack-reservations/": {
"get": {
"operationId": "dcim_rack_reservations_list",
@@ -94278,6 +97358,58 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Group (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Group (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "id",
@@ -94825,6 +97957,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -95187,6 +98328,22 @@
"type": "number"
}
},
+ {
+ "in": "query",
+ "name": "unit_count_max",
+ "schema": {
+ "type": "number"
+ },
+ "description": "Maximum unit count"
+ },
+ {
+ "in": "query",
+ "name": "unit_count_min",
+ "schema": {
+ "type": "number"
+ },
+ "description": "Minimum unit count"
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -96687,6 +99844,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -98921,6 +102087,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "starting_unit",
@@ -100940,6 +104115,60 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Group (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Group (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "id",
@@ -102505,6 +105734,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "starting_unit",
@@ -103926,6 +107164,15 @@
},
"description": "* `json` - json\n* `svg` - svg"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "unit_height",
@@ -105060,6 +108307,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -107674,6 +110930,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -109504,6 +112769,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -111027,6 +114301,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -112923,6 +116206,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -114966,6 +118258,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -116487,6 +119788,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -117540,6 +120850,15 @@
"type": "string"
}
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "user",
@@ -118878,6 +122197,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -120804,6 +124132,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -121774,6 +125111,13 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "debug",
+ "schema": {
+ "type": "boolean"
+ }
+ },
{
"in": "query",
"name": "description",
@@ -122854,6 +126198,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -123619,6 +126972,17 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "choice_colors",
+ "schema": {
+ "type": "array",
+ "items": {}
+ },
+ "description": "Choice colors",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "created",
@@ -124352,6 +127716,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -126362,6 +129735,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "type",
@@ -128941,6 +132323,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -130768,6 +134159,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -132738,6 +136138,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -134126,6 +137535,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -135139,6 +138557,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -135633,6 +139060,15 @@
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
}
],
"tags": [
@@ -136072,6 +139508,15 @@
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
}
],
"tags": [
@@ -137544,6 +140989,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -138413,6 +141867,15 @@
"type": "string"
},
"description": "Search"
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
}
],
"tags": [
@@ -138718,6 +142181,15 @@
"schema": {
"type": "string"
}
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
}
],
"tags": [
@@ -139813,6 +143285,15 @@
"type": "boolean"
}
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "table",
@@ -140783,6 +144264,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -141992,6 +145482,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -143858,6 +147357,15 @@
"type": "boolean"
}
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -145129,6 +148637,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -146714,17 +150231,13 @@
"style": "form"
},
{
- "in": "query",
"name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
"schema": {
- "type": "array",
- "items": {
- "type": "integer",
- "format": "int32"
- }
- },
- "explode": true,
- "style": "form"
+ "type": "integer"
+ }
},
{
"in": "query",
@@ -148235,6 +151748,60 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "role",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Role (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "role__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Role (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "role_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Role (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "role_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Role (ID)",
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "site",
@@ -148335,6 +151902,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -149460,6 +153036,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -151229,6 +154814,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -153038,6 +156632,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -154761,6 +158364,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "start_address",
@@ -156953,6 +160565,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -158998,6 +162619,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -160343,6 +163973,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -161854,6 +165493,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -163356,6 +167004,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -165043,6 +168700,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -166297,6 +169963,13 @@
"type": "integer"
}
},
+ {
+ "in": "query",
+ "name": "rack_group",
+ "schema": {
+ "type": "number"
+ }
+ },
{
"in": "query",
"name": "region",
@@ -166578,6 +170251,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -166728,6 +170410,91 @@
"explode": true,
"style": "form"
},
+ {
+ "in": "query",
+ "name": "total_vlan_ids",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "total_vlan_ids__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "total_vlan_ids__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "total_vlan_ids__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "total_vlan_ids__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "total_vlan_ids__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "total_vlan_ids__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
{
"in": "query",
"name": "updated_by_request",
@@ -167977,6 +171744,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -169136,6 +172912,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -170994,6 +174779,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -172819,6 +176613,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -174234,6 +178037,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -175707,6 +179519,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -177052,6 +180873,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -178898,6 +182728,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -180496,6 +184335,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -181965,6 +185813,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -182989,6 +186846,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "user_id",
@@ -183848,6 +187714,15 @@
"type": "string"
},
"description": "Search"
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
}
],
"tags": [
@@ -184737,6 +188612,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "user",
@@ -185954,6 +189838,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "user",
@@ -187004,6 +190897,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "user",
@@ -188601,6 +192503,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "username",
@@ -190117,6 +194028,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -191462,6 +195382,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -193091,6 +197020,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -195242,6 +199180,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -196697,6 +200644,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -197212,6 +201168,1663 @@
}
}
},
+ "/api/virtualization/virtual-machine-types/": {
+ "get": {
+ "operationId": "virtualization_virtual_machine_types_list",
+ "description": "Get a list of virtual machine type objects.",
+ "parameters": [
+ {
+ "in": "query",
+ "name": "created",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__empty",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "created_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "default_memory",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_memory__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "default_memory__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_memory__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_memory__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_memory__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_memory__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_platform",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_platform__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_platform_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_platform_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "default_vcpus__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "number",
+ "format": "double"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "description__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "description__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "id__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__empty",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "last_updated__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "limit",
+ "required": false,
+ "in": "query",
+ "description": "Number of results to return per page.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "modified_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "name__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "name__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "offset",
+ "required": false,
+ "in": "query",
+ "description": "The initial index from which to return the results.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "name": "ordering",
+ "required": false,
+ "in": "query",
+ "description": "Which field to use when ordering the results.",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "in": "query",
+ "name": "owner",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Owner Group (name)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_group_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "description": "Owner Group (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "owner_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true
+ }
+ },
+ "description": "Owner (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "q",
+ "schema": {
+ "type": "string"
+ },
+ "description": "Search"
+ },
+ {
+ "in": "query",
+ "name": "slug",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "slug__ic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__ie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__iew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__iregex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__isw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__nic",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__nie",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__niew",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__nisw",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "slug__regex",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
+ "in": "query",
+ "name": "tag",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "tag_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "updated_by_request",
+ "schema": {
+ "type": "string",
+ "format": "uuid"
+ }
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count__empty",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count__gt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count__gte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count__lt",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count__lte",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_count__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "explode": true,
+ "style": "form"
+ }
+ ],
+ "tags": [
+ "virtualization"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PaginatedVirtualMachineTypeList"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "post": {
+ "operationId": "virtualization_virtual_machine_types_create",
+ "description": "Post a list of virtual machine type objects.",
+ "tags": [
+ "virtualization"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ ]
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "201": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "virtualization_virtual_machine_types_bulk_update",
+ "description": "Put a list of virtual machine type objects.",
+ "tags": [
+ "virtualization"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "virtualization_virtual_machine_types_bulk_partial_update",
+ "description": "Patch a list of virtual machine type objects.",
+ "tags": [
+ "virtualization"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "virtualization_virtual_machine_types_bulk_destroy",
+ "description": "Delete a list of virtual machine type objects.",
+ "tags": [
+ "virtualization"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
+ "/api/virtualization/virtual-machine-types/{id}/": {
+ "get": {
+ "operationId": "virtualization_virtual_machine_types_retrieve",
+ "description": "Get a virtual machine type object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this virtual machine type.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "virtualization"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "put": {
+ "operationId": "virtualization_virtual_machine_types_update",
+ "description": "Put a virtual machine type object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this virtual machine type.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "virtualization"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/VirtualMachineTypeRequest"
+ }
+ }
+ },
+ "required": true
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "patch": {
+ "operationId": "virtualization_virtual_machine_types_partial_update",
+ "description": "Patch a virtual machine type object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this virtual machine type.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "virtualization"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedVirtualMachineTypeRequest"
+ }
+ },
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/PatchedVirtualMachineTypeRequest"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ },
+ "description": ""
+ }
+ }
+ },
+ "delete": {
+ "operationId": "virtualization_virtual_machine_types_destroy",
+ "description": "Delete a virtual machine type object.",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "id",
+ "schema": {
+ "type": "integer"
+ },
+ "description": "A unique integer value identifying this virtual machine type.",
+ "required": true
+ }
+ ],
+ "tags": [
+ "virtualization"
+ ],
+ "security": [
+ {
+ "cookieAuth": []
+ },
+ {
+ "tokenAuth": []
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No response body"
+ }
+ }
+ }
+ },
"/api/virtualization/virtual-machines/": {
"get": {
"operationId": "virtualization_virtual_machines_list",
@@ -199177,6 +204790,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "start_on_boot",
@@ -199830,6 +205452,62 @@
},
"explode": true,
"style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_type",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Virtual machine type (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_type__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "description": "Virtual machine type (slug)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_type_id",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true,
+ "title": "Type"
+ }
+ },
+ "description": "Virtual machine type (ID)",
+ "explode": true,
+ "style": "form"
+ },
+ {
+ "in": "query",
+ "name": "virtual_machine_type_id__n",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "nullable": true,
+ "title": "Type"
+ }
+ },
+ "description": "Virtual machine type (ID)",
+ "explode": true,
+ "style": "form"
}
],
"tags": [
@@ -201347,6 +207025,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -203503,6 +209190,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -204925,6 +210621,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -206386,6 +212091,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -208176,6 +213890,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -209303,6 +215026,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -211125,6 +216857,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -212974,6 +218715,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -214001,6 +219751,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -215755,6 +221514,15 @@
},
"description": "Search"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -217548,6 +223316,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "tag",
@@ -219644,6 +225421,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -221948,6 +227734,15 @@
"explode": true,
"style": "form"
},
+ {
+ "name": "start",
+ "required": false,
+ "in": "query",
+ "description": "Cursor-based pagination: return results with pk >= start, ordered by pk. Mutually exclusive with offset.",
+ "schema": {
+ "type": "integer"
+ }
+ },
{
"in": "query",
"name": "status",
@@ -222716,6 +228511,14 @@
],
"nullable": true
},
+ "role": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRole"
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"allOf": [
{
@@ -223023,6 +228826,22 @@
],
"nullable": true
},
+ "role": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRoleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"oneOf": [
{
@@ -223768,6 +229587,57 @@
"url"
]
},
+ "BriefCableBundle": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "display",
+ "id",
+ "name",
+ "url"
+ ]
+ },
+ "BriefCableBundleRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "name"
+ ]
+ },
"BriefCableRequest": {
"type": "object",
"description": "Base serializer class for models inheriting from PrimaryModel.",
@@ -224990,6 +230860,21 @@
"address": {
"type": "string"
},
+ "nat_inside": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/NestedIPAddress"
+ }
+ ],
+ "nullable": true
+ },
+ "nat_outside": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedIPAddress"
+ },
+ "readOnly": true
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -225000,6 +230885,7 @@
"display",
"family",
"id",
+ "nat_outside",
"url"
]
},
@@ -225011,6 +230897,14 @@
"type": "string",
"minLength": 1
},
+ "nat_inside": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/NestedIPAddressRequest"
+ }
+ ],
+ "nullable": true
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -226600,6 +232494,76 @@
"url"
]
},
+ "BriefRackGroup": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "rack_count": {
+ "type": "integer",
+ "format": "int64",
+ "readOnly": true
+ }
+ },
+ "required": [
+ "display",
+ "id",
+ "name",
+ "rack_count",
+ "slug",
+ "url"
+ ]
+ },
+ "BriefRackGroupRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "name",
+ "slug"
+ ]
+ },
"BriefRackRequest": {
"type": "object",
"description": "Base serializer class for models inheriting from PrimaryModel.",
@@ -226887,9 +232851,15 @@
"type": "integer",
"format": "int64",
"readOnly": true
+ },
+ "asn_count": {
+ "type": "integer",
+ "format": "int64",
+ "readOnly": true
}
},
"required": [
+ "asn_count",
"display",
"id",
"name",
@@ -227940,6 +233910,70 @@
"name"
]
},
+ "BriefVirtualMachineType": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "display",
+ "id",
+ "name",
+ "slug",
+ "url"
+ ]
+ },
+ "BriefVirtualMachineTypeRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ "required": [
+ "name",
+ "slug"
+ ]
+ },
"BriefWirelessLANGroup": {
"type": "object",
"description": "Base serializer class for models inheriting from NestedGroupModel.",
@@ -228191,6 +234225,14 @@
],
"nullable": true
},
+ "bundle": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefCableBundle"
+ }
+ ],
+ "nullable": true
+ },
"label": {
"type": "string",
"maxLength": 100
@@ -228288,6 +234330,133 @@
"url"
]
},
+ "CableBundle": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display_url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTag"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ },
+ "created": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true,
+ "nullable": true
+ },
+ "last_updated": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true,
+ "nullable": true
+ },
+ "cable_count": {
+ "type": "integer",
+ "readOnly": true,
+ "default": 0
+ }
+ },
+ "required": [
+ "cable_count",
+ "created",
+ "display",
+ "display_url",
+ "id",
+ "last_updated",
+ "name",
+ "url"
+ ]
+ },
+ "CableBundleRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ },
+ "required": [
+ "name"
+ ]
+ },
"CableRequest": {
"type": "object",
"description": "Base serializer class for models inheriting from PrimaryModel.",
@@ -228405,6 +234574,22 @@
],
"nullable": true
},
+ "bundle": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefCableBundleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"label": {
"type": "string",
"maxLength": 100
@@ -230840,6 +237025,10 @@
"type": "boolean",
"description": "Download file as attachment"
},
+ "debug": {
+ "type": "boolean",
+ "description": "Enable verbose error output when rendering this template. Not recommended for production use."
+ },
"data_source": {
"$ref": "#/components/schemas/BriefDataSource"
},
@@ -230944,6 +237133,10 @@
"type": "boolean",
"description": "Download file as attachment"
},
+ "debug": {
+ "type": "boolean",
+ "description": "Enable verbose error output when rendering this template. Not recommended for production use."
+ },
"data_source": {
"oneOf": [
{
@@ -233046,6 +239239,10 @@
"description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.",
"maxLength": 500
},
+ "validation_schema": {
+ "nullable": true,
+ "description": "A JSON schema definition for validating the custom field value"
+ },
"choice_set": {
"allOf": [
{
@@ -233153,6 +239350,29 @@
"minItems": 2
}
},
+ "choice_colors": {
+ "type": "object",
+ "additionalProperties": {
+ "enum": [
+ "blue",
+ "indigo",
+ "purple",
+ "pink",
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "teal",
+ "cyan",
+ "gray",
+ "black",
+ "white"
+ ],
+ "type": "string",
+ "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White",
+ "x-spec-enum-id": "de0a6a124020dfe0"
+ }
+ },
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
@@ -233226,6 +239446,29 @@
"minItems": 2
}
},
+ "choice_colors": {
+ "type": "object",
+ "additionalProperties": {
+ "enum": [
+ "blue",
+ "indigo",
+ "purple",
+ "pink",
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "teal",
+ "cyan",
+ "gray",
+ "black",
+ "white"
+ ],
+ "type": "string",
+ "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White",
+ "x-spec-enum-id": "de0a6a124020dfe0"
+ }
+ },
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
@@ -233398,6 +239641,10 @@
"description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.",
"maxLength": 500
},
+ "validation_schema": {
+ "nullable": true,
+ "description": "A JSON schema definition for validating the custom field value"
+ },
"choice_set": {
"oneOf": [
{
@@ -234386,6 +240633,9 @@
"description": "Physical label",
"maxLength": 64
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -234427,9 +240677,15 @@
"format": "date-time",
"readOnly": true,
"nullable": true
+ },
+ "_occupied": {
+ "type": "boolean",
+ "readOnly": true,
+ "title": " occupied"
}
},
"required": [
+ "_occupied",
"created",
"device",
"display",
@@ -234464,6 +240720,9 @@
"description": "Physical label",
"maxLength": 64
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -234546,6 +240805,9 @@
"description": "Physical label",
"maxLength": 64
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -234598,6 +240860,9 @@
"description": "Physical label",
"maxLength": 64
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -243514,6 +249779,30 @@
"description": "Name of the queue in which this job was enqueued",
"maxLength": 100
},
+ "notifications": {
+ "type": "object",
+ "properties": {
+ "value": {
+ "enum": [
+ "always",
+ "on_failure",
+ "never"
+ ],
+ "type": "string",
+ "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never",
+ "x-spec-enum-id": "57071f4400340a5c"
+ },
+ "label": {
+ "type": "string",
+ "enum": [
+ "Always",
+ "On failure",
+ "Never"
+ ]
+ }
+ },
+ "readOnly": true
+ },
"log_entries": {
"type": "array",
"items": {}
@@ -243527,6 +249816,7 @@
"id",
"job_id",
"name",
+ "notifications",
"object",
"object_type",
"status",
@@ -244819,14 +251109,6 @@
"type": "string",
"maxLength": 64
},
- "installed_module": {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefModule"
- }
- ],
- "nullable": true
- },
"label": {
"type": "string",
"description": "Physical label",
@@ -244837,10 +251119,21 @@
"description": "Identifier to reference when renaming installed components",
"maxLength": 30
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
},
+ "installed_module": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefModule"
+ }
+ ],
+ "nullable": true
+ },
"owner": {
"allOf": [
{
@@ -244870,9 +251163,15 @@
"format": "date-time",
"readOnly": true,
"nullable": true
+ },
+ "_occupied": {
+ "type": "boolean",
+ "readOnly": true,
+ "title": " occupied"
}
},
"required": [
+ "_occupied",
"created",
"device",
"display",
@@ -244918,6 +251217,23 @@
"minLength": 1,
"maxLength": 64
},
+ "label": {
+ "type": "string",
+ "description": "Physical label",
+ "maxLength": 64
+ },
+ "position": {
+ "type": "string",
+ "description": "Identifier to reference when renaming installed components",
+ "maxLength": 30
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
"installed_module": {
"oneOf": [
{
@@ -244934,20 +251250,6 @@
],
"nullable": true
},
- "label": {
- "type": "string",
- "description": "Physical label",
- "maxLength": 64
- },
- "position": {
- "type": "string",
- "description": "Identifier to reference when renaming installed components",
- "maxLength": 30
- },
- "description": {
- "type": "string",
- "maxLength": 200
- },
"owner": {
"oneOf": [
{
@@ -245028,6 +251330,9 @@
"description": "Identifier to reference when renaming installed components",
"maxLength": 30
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -245106,6 +251411,9 @@
"description": "Identifier to reference when renaming installed components",
"maxLength": 30
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -245198,6 +251506,18 @@
"custom_fields": {
"type": "object",
"additionalProperties": {}
+ },
+ "replicate_components": {
+ "type": "boolean",
+ "writeOnly": true,
+ "default": true,
+ "description": "Automatically populate components associated with this module type (default: true)"
+ },
+ "adopt_components": {
+ "type": "boolean",
+ "writeOnly": true,
+ "default": false,
+ "description": "Adopt already existing components"
}
},
"required": [
@@ -247615,6 +253935,37 @@
}
}
},
+ "PaginatedCableBundleList": {
+ "type": "object",
+ "required": [
+ "count",
+ "results"
+ ],
+ "properties": {
+ "count": {
+ "type": "integer",
+ "example": 123
+ },
+ "next": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=400&limit=100"
+ },
+ "previous": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=200&limit=100"
+ },
+ "results": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CableBundle"
+ }
+ }
+ }
+ },
"PaginatedCableList": {
"type": "object",
"required": [
@@ -250157,6 +256508,37 @@
}
}
},
+ "PaginatedRackGroupList": {
+ "type": "object",
+ "required": [
+ "count",
+ "results"
+ ],
+ "properties": {
+ "count": {
+ "type": "integer",
+ "example": 123
+ },
+ "next": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=400&limit=100"
+ },
+ "previous": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=200&limit=100"
+ },
+ "results": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RackGroup"
+ }
+ }
+ }
+ },
"PaginatedRackList": {
"type": "object",
"required": [
@@ -251366,6 +257748,37 @@
}
}
},
+ "PaginatedVirtualMachineTypeList": {
+ "type": "object",
+ "required": [
+ "count",
+ "results"
+ ],
+ "properties": {
+ "count": {
+ "type": "integer",
+ "example": 123
+ },
+ "next": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=400&limit=100"
+ },
+ "previous": {
+ "type": "string",
+ "nullable": true,
+ "format": "uri",
+ "example": "http://api.example.org/accounts/?offset=200&limit=100"
+ },
+ "results": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/VirtualMachineType"
+ }
+ }
+ }
+ },
"PaginatedVirtualMachineWithConfigContextList": {
"type": "object",
"required": [
@@ -251636,6 +258049,22 @@
],
"nullable": true
},
+ "role": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRoleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"oneOf": [
{
@@ -251718,6 +258147,50 @@
}
}
},
+ "PatchedCableBundleRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ }
+ },
"PatchedCircuitGroupRequest": {
"type": "object",
"description": "Base serializer class for models inheriting from OrganizationalModel.",
@@ -252260,6 +258733,10 @@
"type": "boolean",
"description": "Download file as attachment"
},
+ "debug": {
+ "type": "boolean",
+ "description": "Enable verbose error output when rendering this template. Not recommended for production use."
+ },
"data_source": {
"oneOf": [
{
@@ -252533,6 +259010,9 @@
"description": "Physical label",
"maxLength": 64
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -252606,6 +259086,9 @@
"description": "Physical label",
"maxLength": 64
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -253159,6 +259642,23 @@
"minLength": 1,
"maxLength": 64
},
+ "label": {
+ "type": "string",
+ "description": "Physical label",
+ "maxLength": 64
+ },
+ "position": {
+ "type": "string",
+ "description": "Identifier to reference when renaming installed components",
+ "maxLength": 30
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
"installed_module": {
"oneOf": [
{
@@ -253175,20 +259675,6 @@
],
"nullable": true
},
- "label": {
- "type": "string",
- "description": "Physical label",
- "maxLength": 64
- },
- "position": {
- "type": "string",
- "description": "Identifier to reference when renaming installed components",
- "maxLength": 30
- },
- "description": {
- "type": "string",
- "maxLength": 200
- },
"owner": {
"oneOf": [
{
@@ -253269,6 +259755,9 @@
"description": "Identifier to reference when renaming installed components",
"maxLength": 30
},
+ "enabled": {
+ "type": "boolean"
+ },
"description": {
"type": "string",
"maxLength": 200
@@ -253807,6 +260296,56 @@
}
}
},
+ "PatchedRackGroupRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ }
+ },
"PatchedRackRoleRequest": {
"type": "object",
"description": "Base serializer class for models inheriting from OrganizationalModel.",
@@ -254053,6 +260592,17 @@
"interval": {
"type": "integer",
"nullable": true
+ },
+ "notifications": {
+ "enum": [
+ "always",
+ "on_failure",
+ "never"
+ ],
+ "type": "string",
+ "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never",
+ "x-spec-enum-id": "57071f4400340a5c",
+ "default": "always"
}
}
},
@@ -254745,6 +261295,87 @@
}
}
},
+ "PatchedVirtualMachineTypeRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "default_platform": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefPlatformRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "default_vcpus": {
+ "type": "number",
+ "format": "double",
+ "maximum": 10000,
+ "minimum": 0.01,
+ "exclusiveMaximum": true,
+ "nullable": true
+ },
+ "default_memory": {
+ "type": "integer",
+ "maximum": 2147483647,
+ "minimum": 0,
+ "nullable": true,
+ "title": "Default memory (MB)"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ }
+ },
"PatchedWebhookRequest": {
"type": "object",
"description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.",
@@ -255026,6 +261657,22 @@
],
"nullable": true
},
+ "bundle": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefCableBundleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"label": {
"type": "string",
"maxLength": 100
@@ -255956,6 +262603,29 @@
"minItems": 2
}
},
+ "choice_colors": {
+ "type": "object",
+ "additionalProperties": {
+ "enum": [
+ "blue",
+ "indigo",
+ "purple",
+ "pink",
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "teal",
+ "cyan",
+ "gray",
+ "black",
+ "white"
+ ],
+ "type": "string",
+ "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White",
+ "x-spec-enum-id": "de0a6a124020dfe0"
+ }
+ },
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
@@ -256124,6 +262794,10 @@
"description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.",
"maxLength": 500
},
+ "validation_schema": {
+ "nullable": true,
+ "description": "A JSON schema definition for validating the custom field value"
+ },
"choice_set": {
"oneOf": [
{
@@ -259447,6 +266121,18 @@
"custom_fields": {
"type": "object",
"additionalProperties": {}
+ },
+ "replicate_components": {
+ "type": "boolean",
+ "writeOnly": true,
+ "default": true,
+ "description": "Automatically populate components associated with this module type (default: true)"
+ },
+ "adopt_components": {
+ "type": "boolean",
+ "writeOnly": true,
+ "default": false,
+ "description": "Adopt already existing components"
}
}
},
@@ -260788,6 +267474,22 @@
],
"nullable": true
},
+ "group": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRackGroupRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"oneOf": [
{
@@ -262878,6 +269580,38 @@
"minLength": 1,
"maxLength": 64
},
+ "virtual_machine_type": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefVirtualMachineTypeRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "role": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefDeviceRoleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"status": {
"enum": [
"offline",
@@ -262950,43 +269684,6 @@
],
"nullable": true
},
- "serial": {
- "type": "string",
- "title": "Serial number",
- "maxLength": 50
- },
- "role": {
- "oneOf": [
- {
- "type": "integer"
- },
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefDeviceRoleRequest"
- }
- ],
- "nullable": true
- }
- ],
- "nullable": true
- },
- "tenant": {
- "oneOf": [
- {
- "type": "integer"
- },
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefTenantRequest"
- }
- ],
- "nullable": true
- }
- ],
- "nullable": true
- },
"platform": {
"oneOf": [
{
@@ -263059,6 +269756,27 @@
"type": "string",
"maxLength": 200
},
+ "serial": {
+ "type": "string",
+ "title": "Serial number",
+ "maxLength": 50
+ },
+ "tenant": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefTenantRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"owner": {
"oneOf": [
{
@@ -263078,6 +269796,16 @@
"comments": {
"type": "string"
},
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "local_context_data": {
+ "nullable": true,
+ "description": "Local config context data takes precedence over source contexts in the final rendered config context"
+ },
"config_template": {
"oneOf": [
{
@@ -263094,16 +269822,6 @@
],
"nullable": true
},
- "local_context_data": {
- "nullable": true,
- "description": "Local config context data takes precedence over source contexts in the final rendered config context"
- },
- "tags": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/NestedTagRequest"
- }
- },
"custom_fields": {
"type": "object",
"additionalProperties": {}
@@ -267537,6 +274255,14 @@
],
"nullable": true
},
+ "group": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRackGroup"
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"allOf": [
{
@@ -267855,6 +274581,146 @@
"url"
]
},
+ "RackGroup": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display_url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTag"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ },
+ "created": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true,
+ "nullable": true
+ },
+ "last_updated": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true,
+ "nullable": true
+ },
+ "rack_count": {
+ "type": "integer",
+ "format": "int64",
+ "readOnly": true
+ }
+ },
+ "required": [
+ "created",
+ "display",
+ "display_url",
+ "id",
+ "last_updated",
+ "name",
+ "rack_count",
+ "slug",
+ "url"
+ ]
+ },
+ "RackGroupRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from OrganizationalModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ },
+ "required": [
+ "name",
+ "slug"
+ ]
+ },
"RackRequest": {
"type": "object",
"description": "Base serializer class for models inheriting from PrimaryModel.",
@@ -267895,6 +274761,22 @@
],
"nullable": true
},
+ "group": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRackGroupRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"oneOf": [
{
@@ -268164,6 +275046,11 @@
"minimum": 0
}
},
+ "unit_count": {
+ "type": "integer",
+ "format": "int32",
+ "readOnly": true
+ },
"status": {
"type": "object",
"properties": {
@@ -268244,6 +275131,7 @@
"id",
"last_updated",
"rack",
+ "unit_count",
"units",
"url",
"user"
@@ -270145,9 +277033,15 @@
"type": "integer",
"format": "int64",
"readOnly": true
+ },
+ "asn_count": {
+ "type": "integer",
+ "format": "int64",
+ "readOnly": true
}
},
"required": [
+ "asn_count",
"created",
"display",
"display_url",
@@ -270602,6 +277496,17 @@
"interval": {
"type": "integer",
"nullable": true
+ },
+ "notifications": {
+ "enum": [
+ "always",
+ "on_failure",
+ "never"
+ ],
+ "type": "string",
+ "description": "* `always` - Always\n* `on_failure` - On failure\n* `never` - Never",
+ "x-spec-enum-id": "57071f4400340a5c",
+ "default": "always"
}
},
"required": [
@@ -273640,6 +280545,10 @@
"$ref": "#/components/schemas/IntegerRange"
}
},
+ "total_vlan_ids": {
+ "type": "integer",
+ "readOnly": true
+ },
"tenant": {
"allOf": [
{
@@ -273704,6 +280613,7 @@
"name",
"scope",
"slug",
+ "total_vlan_ids",
"url",
"utilization",
"vlan_count"
@@ -275809,6 +282719,199 @@
"virtual_machine"
]
},
+ "VirtualMachineType": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "readOnly": true
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display_url": {
+ "type": "string",
+ "format": "uri",
+ "readOnly": true
+ },
+ "display": {
+ "type": "string",
+ "readOnly": true
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "default_platform": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefPlatform"
+ }
+ ],
+ "nullable": true
+ },
+ "default_vcpus": {
+ "type": "number",
+ "format": "double",
+ "maximum": 10000,
+ "minimum": 0.01,
+ "exclusiveMaximum": true,
+ "nullable": true
+ },
+ "default_memory": {
+ "type": "integer",
+ "maximum": 2147483647,
+ "minimum": 0,
+ "nullable": true,
+ "title": "Default memory (MB)"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwner"
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTag"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ },
+ "created": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true,
+ "nullable": true
+ },
+ "last_updated": {
+ "type": "string",
+ "format": "date-time",
+ "readOnly": true,
+ "nullable": true
+ },
+ "virtual_machine_count": {
+ "type": "integer",
+ "readOnly": true
+ }
+ },
+ "required": [
+ "created",
+ "display",
+ "display_url",
+ "id",
+ "last_updated",
+ "name",
+ "slug",
+ "url",
+ "virtual_machine_count"
+ ]
+ },
+ "VirtualMachineTypeRequest": {
+ "type": "object",
+ "description": "Base serializer class for models inheriting from PrimaryModel.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "slug": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "pattern": "^[-a-zA-Z0-9_]+$"
+ },
+ "default_platform": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefPlatformRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "default_vcpus": {
+ "type": "number",
+ "format": "double",
+ "maximum": 10000,
+ "minimum": 0.01,
+ "exclusiveMaximum": true,
+ "nullable": true
+ },
+ "default_memory": {
+ "type": "integer",
+ "maximum": 2147483647,
+ "minimum": 0,
+ "nullable": true,
+ "title": "Default memory (MB)"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "owner": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefOwnerRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "comments": {
+ "type": "string"
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "custom_fields": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ },
+ "required": [
+ "name",
+ "slug"
+ ]
+ },
"VirtualMachineWithConfigContext": {
"type": "object",
"description": "Base serializer class for models inheriting from PrimaryModel.",
@@ -275835,6 +282938,22 @@
"type": "string",
"maxLength": 64
},
+ "virtual_machine_type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefVirtualMachineType"
+ }
+ ],
+ "nullable": true
+ },
+ "role": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefDeviceRole"
+ }
+ ],
+ "nullable": true
+ },
"status": {
"type": "object",
"properties": {
@@ -275913,27 +283032,6 @@
],
"nullable": true
},
- "serial": {
- "type": "string",
- "title": "Serial number",
- "maxLength": 50
- },
- "role": {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefDeviceRole"
- }
- ],
- "nullable": true
- },
- "tenant": {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefTenant"
- }
- ],
- "nullable": true
- },
"platform": {
"allOf": [
{
@@ -275991,6 +283089,19 @@
"type": "string",
"maxLength": 200
},
+ "serial": {
+ "type": "string",
+ "title": "Serial number",
+ "maxLength": 50
+ },
+ "tenant": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefTenant"
+ }
+ ],
+ "nullable": true
+ },
"owner": {
"allOf": [
{
@@ -276002,6 +283113,16 @@
"comments": {
"type": "string"
},
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTag"
+ }
+ },
+ "local_context_data": {
+ "nullable": true,
+ "description": "Local config context data takes precedence over source contexts in the final rendered config context"
+ },
"config_template": {
"allOf": [
{
@@ -276010,24 +283131,10 @@
],
"nullable": true
},
- "local_context_data": {
- "nullable": true,
- "description": "Local config context data takes precedence over source contexts in the final rendered config context"
- },
- "tags": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/NestedTag"
- }
- },
"custom_fields": {
"type": "object",
"additionalProperties": {}
},
- "config_context": {
- "nullable": true,
- "readOnly": true
- },
"created": {
"type": "string",
"format": "date-time",
@@ -276047,6 +283154,10 @@
"virtual_disk_count": {
"type": "integer",
"readOnly": true
+ },
+ "config_context": {
+ "nullable": true,
+ "readOnly": true
}
},
"required": [
@@ -276072,6 +283183,38 @@
"minLength": 1,
"maxLength": 64
},
+ "virtual_machine_type": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefVirtualMachineTypeRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "role": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefDeviceRoleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"status": {
"enum": [
"offline",
@@ -276144,43 +283287,6 @@
],
"nullable": true
},
- "serial": {
- "type": "string",
- "title": "Serial number",
- "maxLength": 50
- },
- "role": {
- "oneOf": [
- {
- "type": "integer"
- },
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefDeviceRoleRequest"
- }
- ],
- "nullable": true
- }
- ],
- "nullable": true
- },
- "tenant": {
- "oneOf": [
- {
- "type": "integer"
- },
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefTenantRequest"
- }
- ],
- "nullable": true
- }
- ],
- "nullable": true
- },
"platform": {
"oneOf": [
{
@@ -276253,6 +283359,27 @@
"type": "string",
"maxLength": 200
},
+ "serial": {
+ "type": "string",
+ "title": "Serial number",
+ "maxLength": 50
+ },
+ "tenant": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefTenantRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"owner": {
"oneOf": [
{
@@ -276272,6 +283399,16 @@
"comments": {
"type": "string"
},
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "local_context_data": {
+ "nullable": true,
+ "description": "Local config context data takes precedence over source contexts in the final rendered config context"
+ },
"config_template": {
"oneOf": [
{
@@ -276288,16 +283425,6 @@
],
"nullable": true
},
- "local_context_data": {
- "nullable": true,
- "description": "Local config context data takes precedence over source contexts in the final rendered config context"
- },
- "tags": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/NestedTagRequest"
- }
- },
"custom_fields": {
"type": "object",
"additionalProperties": {}
@@ -277559,6 +284686,22 @@
],
"nullable": true
},
+ "bundle": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefCableBundleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"label": {
"type": "string",
"maxLength": 100
@@ -278526,6 +285669,29 @@
"minItems": 2
}
},
+ "choice_colors": {
+ "type": "object",
+ "additionalProperties": {
+ "enum": [
+ "blue",
+ "indigo",
+ "purple",
+ "pink",
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "teal",
+ "cyan",
+ "gray",
+ "black",
+ "white"
+ ],
+ "type": "string",
+ "description": "* `blue` - Blue\n* `indigo` - Indigo\n* `purple` - Purple\n* `pink` - Pink\n* `red` - Red\n* `orange` - Orange\n* `yellow` - Yellow\n* `green` - Green\n* `teal` - Teal\n* `cyan` - Cyan\n* `gray` - Gray\n* `black` - Black\n* `white` - White",
+ "x-spec-enum-id": "de0a6a124020dfe0"
+ }
+ },
"order_alphabetically": {
"type": "boolean",
"description": "Choices are automatically ordered alphabetically"
@@ -278698,6 +285864,10 @@
"description": "Regular expression to enforce on text field values. Use ^ and $ to force matching of entire string. For example, ^[A-Z]{3}$ will limit values to exactly three uppercase letters.",
"maxLength": 500
},
+ "validation_schema": {
+ "nullable": true,
+ "description": "A JSON schema definition for validating the custom field value"
+ },
"choice_set": {
"oneOf": [
{
@@ -282115,6 +289285,18 @@
"custom_fields": {
"type": "object",
"additionalProperties": {}
+ },
+ "replicate_components": {
+ "type": "boolean",
+ "writeOnly": true,
+ "default": true,
+ "description": "Automatically populate components associated with this module type (default: true)"
+ },
+ "adopt_components": {
+ "type": "boolean",
+ "writeOnly": true,
+ "default": false,
+ "description": "Adopt already existing components"
}
},
"required": [
@@ -283490,6 +290672,22 @@
],
"nullable": true
},
+ "group": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefRackGroupRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"tenant": {
"oneOf": [
{
@@ -285666,6 +292864,38 @@
"minLength": 1,
"maxLength": 64
},
+ "virtual_machine_type": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefVirtualMachineTypeRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
+ "role": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefDeviceRoleRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"status": {
"enum": [
"offline",
@@ -285738,43 +292968,6 @@
],
"nullable": true
},
- "serial": {
- "type": "string",
- "title": "Serial number",
- "maxLength": 50
- },
- "role": {
- "oneOf": [
- {
- "type": "integer"
- },
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefDeviceRoleRequest"
- }
- ],
- "nullable": true
- }
- ],
- "nullable": true
- },
- "tenant": {
- "oneOf": [
- {
- "type": "integer"
- },
- {
- "allOf": [
- {
- "$ref": "#/components/schemas/BriefTenantRequest"
- }
- ],
- "nullable": true
- }
- ],
- "nullable": true
- },
"platform": {
"oneOf": [
{
@@ -285847,6 +293040,27 @@
"type": "string",
"maxLength": 200
},
+ "serial": {
+ "type": "string",
+ "title": "Serial number",
+ "maxLength": 50
+ },
+ "tenant": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/BriefTenantRequest"
+ }
+ ],
+ "nullable": true
+ }
+ ],
+ "nullable": true
+ },
"owner": {
"oneOf": [
{
@@ -285866,6 +293080,16 @@
"comments": {
"type": "string"
},
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NestedTagRequest"
+ }
+ },
+ "local_context_data": {
+ "nullable": true,
+ "description": "Local config context data takes precedence over source contexts in the final rendered config context"
+ },
"config_template": {
"oneOf": [
{
@@ -285882,16 +293106,6 @@
],
"nullable": true
},
- "local_context_data": {
- "nullable": true,
- "description": "Local config context data takes precedence over source contexts in the final rendered config context"
- },
- "tags": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/NestedTagRequest"
- }
- },
"custom_fields": {
"type": "object",
"additionalProperties": {}
diff --git a/docs/_theme/partials/copyright.html b/docs/_theme/partials/copyright.html
deleted file mode 100644
index cfbfd5444..000000000
--- a/docs/_theme/partials/copyright.html
+++ /dev/null
@@ -1,18 +0,0 @@
-
{value}')
+ return value
diff --git a/netbox/core/tests/test_changelog.py b/netbox/core/tests/test_changelog.py
index c3924bccb..dc9dbb2dc 100644
--- a/netbox/core/tests/test_changelog.py
+++ b/netbox/core/tests/test_changelog.py
@@ -1,9 +1,16 @@
+import logging
+import uuid
+from datetime import timedelta
+from unittest.mock import patch
+
from django.contrib.contenttypes.models import ContentType
-from django.test import override_settings
+from django.test import TestCase, override_settings
from django.urls import reverse
+from django.utils import timezone
from rest_framework import status
from core.choices import ObjectChangeActionChoices
+from core.jobs import SystemHousekeepingJob
from core.models import ObjectChange, ObjectType
from dcim.choices import InterfaceTypeChoices, ModuleStatusChoices, SiteStatusChoices
from dcim.models import (
@@ -694,3 +701,99 @@ class ChangeLogAPITest(APITestCase):
self.assertEqual(changes[3].changed_object_type, ContentType.objects.get_for_model(Module))
self.assertEqual(changes[3].changed_object_id, module.pk)
self.assertEqual(changes[3].action, ObjectChangeActionChoices.ACTION_DELETE)
+
+
+class ChangelogPruneRetentionTest(TestCase):
+ """Test suite for Changelog pruning retention settings."""
+
+ @staticmethod
+ def _make_oc(*, ct, obj_id, action, ts):
+ oc = ObjectChange.objects.create(
+ changed_object_type=ct,
+ changed_object_id=obj_id,
+ action=action,
+ user_name='test',
+ request_id=uuid.uuid4(),
+ object_repr=f'Object {obj_id}',
+ )
+ ObjectChange.objects.filter(pk=oc.pk).update(time=ts)
+ return oc.pk
+
+ @staticmethod
+ def _run_prune(*, retention_days, retain_create_last_update):
+ job = SystemHousekeepingJob.__new__(SystemHousekeepingJob)
+ job.logger = logging.getLogger('netbox.tests.changelog_prune')
+
+ with patch('core.jobs.Config') as MockConfig:
+ cfg = MockConfig.return_value
+ cfg.CHANGELOG_RETENTION = retention_days
+ cfg.CHANGELOG_RETAIN_CREATE_LAST_UPDATE = retain_create_last_update
+ job.prune_changelog()
+
+ def test_prune_retain_create_last_update_excludes_deleted_objects(self):
+ ct = ContentType.objects.get_for_model(Site)
+
+ retention_days = 90
+ now = timezone.now()
+ cutoff = now - timedelta(days=retention_days)
+
+ expired_old = cutoff - timedelta(days=10)
+ expired_newer = cutoff - timedelta(days=1)
+ not_expired = cutoff + timedelta(days=1)
+
+ # A) Not deleted: should keep CREATE + latest UPDATE, prune intermediate UPDATEs
+ a_create = self._make_oc(ct=ct, obj_id=1, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired_old)
+ a_update1 = self._make_oc(ct=ct, obj_id=1, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_old)
+ a_update2 = self._make_oc(ct=ct, obj_id=1, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_newer)
+
+ # B) Deleted (all expired): should keep NOTHING
+ b_create = self._make_oc(ct=ct, obj_id=2, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired_old)
+ b_update = self._make_oc(ct=ct, obj_id=2, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_newer)
+ b_delete = self._make_oc(ct=ct, obj_id=2, action=ObjectChangeActionChoices.ACTION_DELETE, ts=expired_newer)
+
+ # C) Deleted but delete is not expired: create/update expired should be pruned; delete remains
+ c_create = self._make_oc(ct=ct, obj_id=3, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired_old)
+ c_update = self._make_oc(ct=ct, obj_id=3, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired_newer)
+ c_delete = self._make_oc(ct=ct, obj_id=3, action=ObjectChangeActionChoices.ACTION_DELETE, ts=not_expired)
+
+ self._run_prune(retention_days=retention_days, retain_create_last_update=True)
+
+ remaining = set(ObjectChange.objects.values_list('pk', flat=True))
+
+ # A) Not deleted -> create + latest update remain
+ self.assertIn(a_create, remaining)
+ self.assertIn(a_update2, remaining)
+ self.assertNotIn(a_update1, remaining)
+
+ # B) Deleted (all expired) -> nothing remains
+ self.assertNotIn(b_create, remaining)
+ self.assertNotIn(b_update, remaining)
+ self.assertNotIn(b_delete, remaining)
+
+ # C) Deleted, delete not expired -> delete remains, but create/update are pruned
+ self.assertNotIn(c_create, remaining)
+ self.assertNotIn(c_update, remaining)
+ self.assertIn(c_delete, remaining)
+
+ def test_prune_disabled_deletes_all_expired(self):
+ ct = ContentType.objects.get_for_model(Site)
+
+ retention_days = 90
+ now = timezone.now()
+ cutoff = now - timedelta(days=retention_days)
+ expired = cutoff - timedelta(days=1)
+ not_expired = cutoff + timedelta(days=1)
+
+ # expired create/update should be deleted when feature disabled
+ x_create = self._make_oc(ct=ct, obj_id=10, action=ObjectChangeActionChoices.ACTION_CREATE, ts=expired)
+ x_update = self._make_oc(ct=ct, obj_id=10, action=ObjectChangeActionChoices.ACTION_UPDATE, ts=expired)
+
+ # non-expired delete should remain regardless
+ y_delete = self._make_oc(ct=ct, obj_id=11, action=ObjectChangeActionChoices.ACTION_DELETE, ts=not_expired)
+
+ self._run_prune(retention_days=retention_days, retain_create_last_update=False)
+
+ remaining = set(ObjectChange.objects.values_list('pk', flat=True))
+ self.assertNotIn(x_create, remaining)
+ self.assertNotIn(x_update, remaining)
+ self.assertIn(y_delete, remaining)
diff --git a/netbox/core/tests/test_models.py b/netbox/core/tests/test_models.py
index 2d7dce923..af342324f 100644
--- a/netbox/core/tests/test_models.py
+++ b/netbox/core/tests/test_models.py
@@ -1,13 +1,16 @@
+import uuid
from unittest.mock import MagicMock, patch
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase
-from core.choices import ObjectChangeActionChoices
+from core.choices import JobNotificationChoices, JobStatusChoices, ObjectChangeActionChoices
from core.models import DataSource, Job, ObjectType
from dcim.models import Device, Location, Site
+from extras.models import Notification
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
+from users.models import User
class DataSourceIgnoreRulesTestCase(TestCase):
@@ -226,6 +229,18 @@ class ObjectTypeTest(TestCase):
class JobTest(TestCase):
+ def _make_job(self, user, notifications):
+ """
+ Create and return a persisted Job with the given user and notifications setting.
+ """
+ return Job.objects.create(
+ name='Test Job',
+ job_id=uuid.uuid4(),
+ user=user,
+ notifications=notifications,
+ status=JobStatusChoices.STATUS_RUNNING,
+ )
+
@patch('core.models.jobs.django_rq.get_queue')
def test_delete_cancels_job_from_correct_queue(self, mock_get_queue):
"""
@@ -257,3 +272,75 @@ class JobTest(TestCase):
mock_get_queue.assert_called_with(custom_queue)
mock_queue.fetch_job.assert_called_with(str(job.job_id))
mock_rq_job.cancel.assert_called_once()
+
+ @patch('core.models.jobs.job_end')
+ def test_terminate_notification_always(self, mock_job_end):
+ """
+ With notifications=always, a Notification should be created for every
+ terminal status (completed, failed, errored).
+ """
+ user = User.objects.create_user(username='notification-always')
+
+ for status in (
+ JobStatusChoices.STATUS_COMPLETED,
+ JobStatusChoices.STATUS_FAILED,
+ JobStatusChoices.STATUS_ERRORED,
+ ):
+ with self.subTest(status=status):
+ job = self._make_job(user, JobNotificationChoices.NOTIFICATION_ALWAYS)
+ job.terminate(status=status)
+ self.assertEqual(
+ Notification.objects.filter(user=user, object_id=job.pk).count(),
+ 1,
+ msg=f"Expected a notification for status={status} with notifications=always",
+ )
+
+ @patch('core.models.jobs.job_end')
+ def test_terminate_notification_on_failure(self, mock_job_end):
+ """
+ With notifications=on_failure, a Notification should be created only for
+ non-completed terminal statuses (failed, errored), not for completed.
+ """
+ user = User.objects.create_user(username='notification-on-failure')
+
+ # No notification on successful completion
+ job = self._make_job(user, JobNotificationChoices.NOTIFICATION_ON_FAILURE)
+ job.terminate(status=JobStatusChoices.STATUS_COMPLETED)
+ self.assertEqual(
+ Notification.objects.filter(user=user, object_id=job.pk).count(),
+ 0,
+ msg="Expected no notification for status=completed with notifications=on_failure",
+ )
+
+ # Notification on failure/error
+ for status in (JobStatusChoices.STATUS_FAILED, JobStatusChoices.STATUS_ERRORED):
+ with self.subTest(status=status):
+ job = self._make_job(user, JobNotificationChoices.NOTIFICATION_ON_FAILURE)
+ job.terminate(status=status)
+ self.assertEqual(
+ Notification.objects.filter(user=user, object_id=job.pk).count(),
+ 1,
+ msg=f"Expected a notification for status={status} with notifications=on_failure",
+ )
+
+ @patch('core.models.jobs.job_end')
+ def test_terminate_notification_never(self, mock_job_end):
+ """
+ With notifications=never, no Notification should be created regardless
+ of terminal status.
+ """
+ user = User.objects.create_user(username='notification-never')
+
+ for status in (
+ JobStatusChoices.STATUS_COMPLETED,
+ JobStatusChoices.STATUS_FAILED,
+ JobStatusChoices.STATUS_ERRORED,
+ ):
+ with self.subTest(status=status):
+ job = self._make_job(user, JobNotificationChoices.NOTIFICATION_NEVER)
+ job.terminate(status=status)
+ self.assertEqual(
+ Notification.objects.filter(user=user, object_id=job.pk).count(),
+ 0,
+ msg=f"Expected no notification for status={status} with notifications=never",
+ )
diff --git a/netbox/core/views.py b/netbox/core/views.py
index e419abb1a..fe663cdaf 100644
--- a/netbox/core/views.py
+++ b/netbox/core/views.py
@@ -50,7 +50,7 @@ from netbox.views import generic
from netbox.views.generic.base import BaseObjectView
from netbox.views.generic.mixins import TableMixin
from utilities.apps import get_installed_apps
-from utilities.data import shallow_compare_dict
+from utilities.data import deep_compare_dict
from utilities.forms import ConfirmationForm
from utilities.htmx import htmx_partial
from utilities.json import ConfigJSONEncoder
@@ -103,6 +103,7 @@ class DataSourceView(GetRelatedModelsMixin, generic.ObjectView):
ObjectsTablePanel(
model='core.DataFile',
filters={'source_id': lambda ctx: ctx['object'].pk},
+ exclude_columns=['source'],
),
],
)
@@ -370,17 +371,14 @@ class ObjectChangeView(generic.ObjectView):
prechange_data = instance.prechange_data_clean
if prechange_data and instance.postchange_data:
- diff_added = shallow_compare_dict(
- prechange_data or dict(),
- instance.postchange_data_clean or dict(),
+ diff_added, diff_removed = deep_compare_dict(
+ prechange_data,
+ instance.postchange_data_clean,
exclude=['last_updated'],
)
- diff_removed = {
- x: prechange_data.get(x) for x in diff_added
- } if prechange_data else {}
else:
- diff_added = None
- diff_removed = None
+ diff_added = {}
+ diff_removed = {}
return {
'diff_added': diff_added,
diff --git a/netbox/dcim/api/serializers_/cables.py b/netbox/dcim/api/serializers_/cables.py
index 1f23815ed..1f2eb6b5d 100644
--- a/netbox/dcim/api/serializers_/cables.py
+++ b/netbox/dcim/api/serializers_/cables.py
@@ -3,7 +3,7 @@ from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.choices import *
-from dcim.models import Cable, CablePath, CableTermination
+from dcim.models import Cable, CableBundle, CablePath, CableTermination
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.gfk_fields import GFKSerializerField
from netbox.api.serializers import (
@@ -16,6 +16,7 @@ from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
__all__ = (
+ 'CableBundleSerializer',
'CablePathSerializer',
'CableSerializer',
'CableTerminationSerializer',
@@ -24,6 +25,18 @@ __all__ = (
)
+class CableBundleSerializer(PrimaryModelSerializer):
+ cable_count = serializers.IntegerField(read_only=True, default=0)
+
+ class Meta:
+ model = CableBundle
+ fields = [
+ 'id', 'url', 'display_url', 'display', 'name', 'description', 'owner', 'comments', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'cable_count',
+ ]
+ brief_fields = ('id', 'url', 'display', 'name', 'description')
+
+
class CableSerializer(PrimaryModelSerializer):
a_terminations = GenericObjectSerializer(many=True, required=False)
b_terminations = GenericObjectSerializer(many=True, required=False)
@@ -31,12 +44,13 @@ class CableSerializer(PrimaryModelSerializer):
profile = ChoiceField(choices=CableProfileChoices, required=False)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False, allow_null=True)
+ bundle = CableBundleSerializer(nested=True, required=False, allow_null=True, default=None)
class Meta:
model = Cable
fields = [
'id', 'url', 'display_url', 'display', 'type', 'a_terminations', 'b_terminations', 'status', 'profile',
- 'tenant', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
+ 'tenant', 'bundle', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'label', 'description')
diff --git a/netbox/dcim/api/serializers_/device_components.py b/netbox/dcim/api/serializers_/device_components.py
index 09a850ad5..60e624a71 100644
--- a/netbox/dcim/api/serializers_/device_components.py
+++ b/netbox/dcim/api/serializers_/device_components.py
@@ -423,27 +423,29 @@ class ModuleBaySerializer(OwnerMixin, NetBoxModelSerializer):
required=False,
allow_null=True
)
+ _occupied = serializers.BooleanField(required=False, read_only=True)
class Meta:
model = ModuleBay
fields = [
- 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'installed_module', 'label', 'position',
- 'description', 'owner', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'position', 'enabled',
+ 'description', 'installed_module', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
- brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'description')
+ brief_fields = ('id', 'url', 'display', 'installed_module', 'name', 'enabled', 'description', '_occupied')
class DeviceBaySerializer(OwnerMixin, NetBoxModelSerializer):
device = DeviceSerializer(nested=True)
installed_device = DeviceSerializer(nested=True, required=False, allow_null=True)
+ _occupied = serializers.BooleanField(required=False, read_only=True)
class Meta:
model = DeviceBay
fields = [
- 'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'description', 'installed_device',
- 'owner', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'id', 'url', 'display_url', 'display', 'device', 'name', 'label', 'enabled', 'description',
+ 'installed_device', 'owner', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
- brief_fields = ('id', 'url', 'display', 'device', 'name', 'description')
+ brief_fields = ('id', 'url', 'display', 'device', 'name', 'enabled', 'description', '_occupied',)
class InventoryItemSerializer(OwnerMixin, NetBoxModelSerializer):
diff --git a/netbox/dcim/api/serializers_/devices.py b/netbox/dcim/api/serializers_/devices.py
index 1154374c1..51cb6c3f2 100644
--- a/netbox/dcim/api/serializers_/devices.py
+++ b/netbox/dcim/api/serializers_/devices.py
@@ -58,10 +58,30 @@ class DeviceSerializer(PrimaryModelSerializer):
)
status = ChoiceField(choices=DeviceStatusChoices, required=False)
airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False)
- primary_ip = IPAddressSerializer(nested=True, read_only=True, allow_null=True)
- primary_ip4 = IPAddressSerializer(nested=True, required=False, allow_null=True)
- primary_ip6 = IPAddressSerializer(nested=True, required=False, allow_null=True)
- oob_ip = IPAddressSerializer(nested=True, required=False, allow_null=True)
+ primary_ip = IPAddressSerializer(
+ nested=True,
+ read_only=True,
+ allow_null=True,
+ fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'],
+ )
+ primary_ip4 = IPAddressSerializer(
+ nested=True,
+ required=False,
+ allow_null=True,
+ fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'],
+ )
+ primary_ip6 = IPAddressSerializer(
+ nested=True,
+ required=False,
+ allow_null=True,
+ fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'],
+ )
+ oob_ip = IPAddressSerializer(
+ nested=True,
+ required=False,
+ allow_null=True,
+ fields=[*IPAddressSerializer.Meta.brief_fields, 'nat_inside', 'nat_outside'],
+ )
parent_device = serializers.SerializerMethodField()
cluster = ClusterSerializer(nested=True, required=False, allow_null=True)
virtual_chassis = VirtualChassisSerializer(nested=True, required=False, allow_null=True, default=None)
@@ -151,48 +171,80 @@ class ModuleSerializer(PrimaryModelSerializer):
module_bay = NestedModuleBaySerializer()
module_type = ModuleTypeSerializer(nested=True)
status = ChoiceField(choices=ModuleStatusChoices, required=False)
+ replicate_components = serializers.BooleanField(
+ required=False,
+ default=True,
+ write_only=True,
+ label=_('Replicate components'),
+ help_text=_('Automatically populate components associated with this module type (default: true)')
+ )
+ adopt_components = serializers.BooleanField(
+ required=False,
+ default=False,
+ write_only=True,
+ label=_('Adopt components'),
+ help_text=_('Adopt already existing components')
+ )
class Meta:
model = Module
fields = [
'id', 'url', 'display_url', 'display', 'device', 'module_bay', 'module_type', 'status', 'serial',
'asset_tag', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+ 'replicate_components', 'adopt_components',
]
brief_fields = ('id', 'url', 'display', 'device', 'module_bay', 'module_type', 'description')
def validate(self, data):
+ # When used as a nested serializer (e.g. as the `module` field on device component
+ # serializers), `data` is already a resolved Module instance — skip our custom logic.
+ if self.nested:
+ return super().validate(data)
+
+ # Pop write-only transient fields before ValidatedModelSerializer tries to
+ # construct a Module instance for full_clean(); restore them afterwards.
+ replicate_components = data.pop('replicate_components', True)
+ adopt_components = data.pop('adopt_components', False)
data = super().validate(data)
- if self.nested:
+ # For updates these fields are not meaningful; omit them from validated_data so that
+ # ModelSerializer.update() does not set unexpected attributes on the instance.
+ if self.instance:
return data
- # Skip validation for existing modules (updates)
- if self.instance is not None:
+ # Always pass the flags to create() so it can set the correct private attributes.
+ data['replicate_components'] = replicate_components
+ data['adopt_components'] = adopt_components
+
+ # Skip conflict checks when no component operations are requested.
+ if not replicate_components and not adopt_components:
return data
- module_bay = data.get('module_bay')
- module_type = data.get('module_type')
device = data.get('device')
+ module_type = data.get('module_type')
+ module_bay = data.get('module_bay')
- if not all((module_bay, module_type, device)):
+ # Required-field validation fires separately; skip here if any are missing.
+ if not all([device, module_type, module_bay]):
return data
positions = get_module_bay_positions(module_bay)
- for templates, component_attribute in [
- ("consoleporttemplates", "consoleports"),
- ("consoleserverporttemplates", "consoleserverports"),
- ("interfacetemplates", "interfaces"),
- ("powerporttemplates", "powerports"),
- ("poweroutlettemplates", "poweroutlets"),
- ("rearporttemplates", "rearports"),
- ("frontporttemplates", "frontports"),
+ for templates_attr, component_attr in [
+ ('consoleporttemplates', 'consoleports'),
+ ('consoleserverporttemplates', 'consoleserverports'),
+ ('interfacetemplates', 'interfaces'),
+ ('powerporttemplates', 'powerports'),
+ ('poweroutlettemplates', 'poweroutlets'),
+ ('rearporttemplates', 'rearports'),
+ ('frontporttemplates', 'frontports'),
]:
installed_components = {
- component.name: component for component in getattr(device, component_attribute).all()
+ component.name: component
+ for component in getattr(device, component_attr).all()
}
- for template in getattr(module_type, templates).all():
+ for template in getattr(module_type, templates_attr).all():
resolved_name = template.name
if MODULE_TOKEN in template.name:
if not module_bay.position:
@@ -204,7 +256,17 @@ class ModuleSerializer(PrimaryModelSerializer):
except ValueError as e:
raise serializers.ValidationError(str(e))
- if resolved_name in installed_components:
+ existing_item = installed_components.get(resolved_name)
+
+ if adopt_components and existing_item and existing_item.module:
+ raise serializers.ValidationError(
+ _("Cannot adopt {model} {name} as it already belongs to a module").format(
+ model=template.component_model.__name__,
+ name=resolved_name
+ )
+ )
+
+ if not adopt_components and resolved_name in installed_components:
raise serializers.ValidationError(
_("A {model} named {name} already exists").format(
model=template.component_model.__name__,
@@ -214,6 +276,27 @@ class ModuleSerializer(PrimaryModelSerializer):
return data
+ def create(self, validated_data):
+ replicate_components = validated_data.pop('replicate_components', True)
+ adopt_components = validated_data.pop('adopt_components', False)
+
+ # Tags are handled after save; pop them here to pass to _save_tags()
+ tags = validated_data.pop('tags', None)
+
+ # _adopt_components and _disable_replication must be set on the instance before
+ # save() is called, so we cannot delegate to super().create() here.
+ instance = self.Meta.model(**validated_data)
+ if adopt_components:
+ instance._adopt_components = True
+ if not replicate_components:
+ instance._disable_replication = True
+ instance.save()
+
+ if tags is not None:
+ self._save_tags(instance, tags)
+
+ return instance
+
class MACAddressSerializer(PrimaryModelSerializer):
assigned_object_type = ContentTypeField(
diff --git a/netbox/dcim/api/serializers_/devicetype_components.py b/netbox/dcim/api/serializers_/devicetype_components.py
index 147bc1701..b9b9c95ee 100644
--- a/netbox/dcim/api/serializers_/devicetype_components.py
+++ b/netbox/dcim/api/serializers_/devicetype_components.py
@@ -317,10 +317,10 @@ class ModuleBayTemplateSerializer(ComponentTemplateSerializer):
class Meta:
model = ModuleBayTemplate
fields = [
- 'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'position', 'description',
+ 'id', 'url', 'display', 'device_type', 'module_type', 'name', 'label', 'position', 'enabled', 'description',
'created', 'last_updated',
]
- brief_fields = ('id', 'url', 'display', 'name', 'description')
+ brief_fields = ('id', 'url', 'display', 'name', 'enabled', 'description')
class DeviceBayTemplateSerializer(ComponentTemplateSerializer):
@@ -331,10 +331,10 @@ class DeviceBayTemplateSerializer(ComponentTemplateSerializer):
class Meta:
model = DeviceBayTemplate
fields = [
- 'id', 'url', 'display', 'device_type', 'name', 'label', 'description',
+ 'id', 'url', 'display', 'device_type', 'name', 'label', 'enabled', 'description',
'created', 'last_updated'
]
- brief_fields = ('id', 'url', 'display', 'name', 'description')
+ brief_fields = ('id', 'url', 'display', 'name', 'enabled', 'description')
class InventoryItemTemplateSerializer(ComponentTemplateSerializer):
diff --git a/netbox/dcim/api/serializers_/racks.py b/netbox/dcim/api/serializers_/racks.py
index b87140bed..2a4bd0834 100644
--- a/netbox/dcim/api/serializers_/racks.py
+++ b/netbox/dcim/api/serializers_/racks.py
@@ -1,9 +1,11 @@
from django.utils.translation import gettext as _
+from drf_spectacular.types import OpenApiTypes
+from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.choices import *
from dcim.constants import *
-from dcim.models import Rack, RackReservation, RackRole, RackType
+from dcim.models import Rack, RackGroup, RackReservation, RackRole, RackType
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import OrganizationalModelSerializer, PrimaryModelSerializer
from netbox.choices import *
@@ -16,6 +18,7 @@ from .sites import LocationSerializer, SiteSerializer
__all__ = (
'RackElevationDetailFilterSerializer',
+ 'RackGroupSerializer',
'RackReservationSerializer',
'RackRoleSerializer',
'RackSerializer',
@@ -23,6 +26,20 @@ __all__ = (
)
+class RackGroupSerializer(OrganizationalModelSerializer):
+
+ # Related object counts
+ rack_count = RelatedObjectCountField('racks')
+
+ class Meta:
+ model = RackGroup
+ fields = [
+ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'owner', 'comments', 'tags',
+ 'custom_fields', 'created', 'last_updated', 'rack_count',
+ ]
+ brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count')
+
+
class RackRoleSerializer(OrganizationalModelSerializer):
# Related object counts
@@ -87,6 +104,11 @@ class RackSerializer(RackBaseSerializer):
allow_null=True,
default=None
)
+ group = RackGroupSerializer(
+ nested=True,
+ required=False,
+ allow_null=True
+ )
tenant = TenantSerializer(
nested=True,
required=False,
@@ -127,11 +149,11 @@ class RackSerializer(RackBaseSerializer):
class Meta:
model = Rack
fields = [
- 'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status',
- 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'weight',
- 'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit',
- 'mounting_depth', 'airflow', 'description', 'owner', 'comments', 'tags', 'custom_fields', 'created',
- 'last_updated', 'device_count', 'powerfeed_count',
+ 'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'group', 'tenant',
+ 'status', 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit',
+ 'weight', 'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth',
+ 'outer_unit', 'mounting_depth', 'airflow', 'description', 'owner', 'comments', 'tags', 'custom_fields',
+ 'created', 'last_updated', 'device_count', 'powerfeed_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count')
@@ -153,11 +175,17 @@ class RackReservationSerializer(PrimaryModelSerializer):
allow_null=True,
)
+ unit_count = serializers.SerializerMethodField()
+
+ @extend_schema_field(OpenApiTypes.INT32)
+ def get_unit_count(self, obj):
+ return len(obj.units)
+
class Meta:
model = RackReservation
fields = [
- 'id', 'url', 'display_url', 'display', 'rack', 'units', 'status', 'created', 'last_updated', 'user',
- 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields',
+ 'id', 'url', 'display_url', 'display', 'rack', 'units', 'unit_count', 'status', 'created', 'last_updated',
+ 'user', 'tenant', 'description', 'owner', 'comments', 'tags', 'custom_fields',
]
brief_fields = ('id', 'url', 'display', 'status', 'user', 'description', 'units')
diff --git a/netbox/dcim/api/urls.py b/netbox/dcim/api/urls.py
index e20e36c2f..852a871bb 100644
--- a/netbox/dcim/api/urls.py
+++ b/netbox/dcim/api/urls.py
@@ -12,6 +12,7 @@ router.register('sites', views.SiteViewSet)
# Racks
router.register('locations', views.LocationViewSet)
+router.register('rack-groups', views.RackGroupViewSet)
router.register('rack-types', views.RackTypeViewSet)
router.register('rack-roles', views.RackRoleViewSet)
router.register('racks', views.RackViewSet)
@@ -63,6 +64,7 @@ router.register('mac-addresses', views.MACAddressViewSet)
# Cables
router.register('cables', views.CableViewSet)
router.register('cable-terminations', views.CableTerminationViewSet)
+router.register('cable-bundles', views.CableBundleViewSet)
# Virtual chassis
router.register('virtual-chassis', views.VirtualChassisViewSet)
diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py
index 6cf8e7a3d..3efa63acb 100644
--- a/netbox/dcim/api/views.py
+++ b/netbox/dcim/api/views.py
@@ -19,6 +19,7 @@ from netbox.api.pagination import StripCountAnnotationsPaginator
from netbox.api.viewsets import MPTTLockedMixin, NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
from netbox.api.viewsets.mixins import SequentialBulkCreatesMixin
from utilities.api import get_serializer_for_model
+from utilities.query import count_related
from utilities.query_functions import CollateAsChar
from virtualization.models import VirtualMachine
@@ -154,6 +155,17 @@ class LocationViewSet(MPTTLockedMixin, NetBoxModelViewSet):
filterset_class = filtersets.LocationFilterSet
+#
+# Rack groups
+#
+
+
+class RackGroupViewSet(NetBoxModelViewSet):
+ queryset = RackGroup.objects.all()
+ serializer_class = serializers.RackGroupSerializer
+ filterset_class = filtersets.RackGroupFilterSet
+
+
#
# Rack roles
#
@@ -574,6 +586,14 @@ class CableTerminationViewSet(NetBoxReadOnlyModelViewSet):
filterset_class = filtersets.CableTerminationFilterSet
+class CableBundleViewSet(NetBoxModelViewSet):
+ queryset = CableBundle.objects.annotate(
+ cable_count=count_related(Cable, 'bundle')
+ )
+ serializer_class = serializers.CableBundleSerializer
+ filterset_class = filtersets.CableBundleFilterSet
+
+
#
# Virtual chassis
#
diff --git a/netbox/dcim/cable_profiles.py b/netbox/dcim/cable_profiles.py
index 8945370f2..921392dc0 100644
--- a/netbox/dcim/cable_profiles.py
+++ b/netbox/dcim/cable_profiles.py
@@ -1,9 +1,14 @@
+from collections import defaultdict, namedtuple
+
from django.core.exceptions import ValidationError
+from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from dcim.choices import CableEndChoices
from dcim.models import CableTermination
+PeerLookupKey = namedtuple('PeerLookupKey', ['cable_id', 'cable_end', 'connector', 'position'])
+
class BaseCableProfile:
"""Base class for representing a cable profile."""
@@ -55,9 +60,14 @@ class BaseCableProfile:
return self._mapping.get((connector, position))
return connector, position
- def get_peer_termination(self, termination, position):
+ #
+ # Peer termination resolution
+ #
+
+ def _get_peer_lookup_key(self, termination, position):
"""
- Given a terminating object, return the peer terminating object (if any) on the opposite end of the cable.
+ Given a cabled object and a local position, return the (cable_id, cable_end, connector, position)
+ tuple identifying the corresponding peer CableTermination on the opposite end of the cable.
"""
try:
connector, position = self.get_mapped_position(
@@ -70,10 +80,17 @@ class BaseCableProfile:
f"Could not map connector {termination.cable_connector} position {position} on side "
f"{termination.cable_end}"
)
+ return PeerLookupKey(termination.cable_id, termination.opposite_cable_end, connector, position)
+
+ def get_peer_termination(self, termination, position):
+ """
+ Given a terminating object, return the peer terminating object (if any) on the opposite end of the cable.
+ """
+ cable_id, cable_end, connector, position = self._get_peer_lookup_key(termination, position)
try:
ct = CableTermination.objects.get(
- cable=termination.cable,
- cable_end=termination.opposite_cable_end,
+ cable_id=cable_id,
+ cable_end=cable_end,
connector=connector,
positions__contains=[position],
)
@@ -81,6 +98,81 @@ class BaseCableProfile:
except CableTermination.DoesNotExist:
return None, None
+ def get_peer_terminations(self, term_position_pairs):
+ """
+ Resolve a batch of (termination, position) pairs to peer terminations.
+ """
+ if not term_position_pairs:
+ return []
+
+ # Fast path: a single pair doesn't benefit from batching
+ if len(term_position_pairs) == 1:
+ return [self.get_peer_termination(*term_position_pairs[0])]
+
+ lookup_keys = [
+ self._get_peer_lookup_key(termination, position) for termination, position in term_position_pairs
+ ]
+
+ # Group requested positions by (cable_id, cable_end, connector) so we can
+ # build one overlap clause per group instead of one clause per position.
+ positions_by_group = defaultdict(set)
+ for key in lookup_keys:
+ positions_by_group[(key.cable_id, key.cable_end, key.connector)].add(key.position)
+
+ q_filter = Q()
+ for (cable_id, cable_end, connector), positions in positions_by_group.items():
+ q_filter |= Q(
+ cable_id=cable_id,
+ cable_end=cable_end,
+ connector=connector,
+ positions__overlap=list(positions),
+ )
+
+ peer_ct_by_key = {}
+ term_ids_by_type = defaultdict(set)
+ model_by_type = {}
+
+ for ct in CableTermination.objects.filter(q_filter).select_related('termination_type'):
+ model_by_type[ct.termination_type_id] = ct.termination_type.model_class()
+ term_ids_by_type[ct.termination_type_id].add(ct.termination_id)
+
+ group_key = (ct.cable_id, ct.cable_end, ct.connector)
+ requested_positions = positions_by_group.get(group_key, ())
+
+ # The overlap query may return a termination carrying additional
+ # positions, so only index the positions we actually requested.
+ for pos in ct.positions or []:
+ if pos not in requested_positions:
+ continue
+
+ key = PeerLookupKey(ct.cable_id, ct.cable_end, ct.connector, pos)
+ if key in peer_ct_by_key and peer_ct_by_key[key].pk != ct.pk:
+ raise CableTermination.MultipleObjectsReturned(
+ f"Multiple peer terminations found for cable {ct.cable_id} end {ct.cable_end} "
+ f"connector {ct.connector} position {pos}"
+ )
+ peer_ct_by_key[key] = ct
+
+ # Use _base_manager to ensure all termination objects are loaded
+ # regardless of any custom default manager filters (e.g. soft-delete).
+ # Note: Django's GenericForeignKey / ContentType.get_object_for_this_type()
+ # uses _default_manager, but _base_manager is safer for bulk resolution
+ # during path tracing.
+ term_by_type = {
+ type_id: model_by_type[type_id]._base_manager.in_bulk(ids) for type_id, ids in term_ids_by_type.items()
+ }
+
+ results = []
+ for key in lookup_keys:
+ ct = peer_ct_by_key.get(key)
+ if ct is None:
+ results.append((None, None))
+ else:
+ termination = term_by_type[ct.termination_type_id].get(ct.termination_id)
+ results.append((termination, key.position if termination is not None else None))
+
+ return results
+
@staticmethod
def get_position_list(n):
"""Return a list of integers from 1 to n, inclusive."""
diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py
index 669345d7c..87f8844e0 100644
--- a/netbox/dcim/constants.py
+++ b/netbox/dcim/constants.py
@@ -1,3 +1,5 @@
+import re
+
from django.db.models import Q
from .choices import InterfaceTypeChoices
@@ -79,6 +81,7 @@ NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES
#
MODULE_TOKEN = '{module}'
+VC_POSITION_RE = re.compile(r'\{vc_position(?::([^}]*))?\}')
MODULAR_COMPONENT_TEMPLATE_MODELS = Q(
app_label='dcim',
diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py
index 5e8bf6481..05a9751dc 100644
--- a/netbox/dcim/filtersets.py
+++ b/netbox/dcim/filtersets.py
@@ -1,6 +1,7 @@
import django_filters
import netaddr
from django.contrib.contenttypes.models import ContentType
+from django.db.models import Func, IntegerField
from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
@@ -46,6 +47,7 @@ from .constants import *
from .models import *
__all__ = (
+ 'CableBundleFilterSet',
'CableFilterSet',
'CableTerminationFilterSet',
'CabledObjectFilterSet',
@@ -86,6 +88,7 @@ __all__ = (
'PowerPortFilterSet',
'PowerPortTemplateFilterSet',
'RackFilterSet',
+ 'RackGroupFilterSet',
'RackReservationFilterSet',
'RackRoleFilterSet',
'RackTypeFilterSet',
@@ -313,6 +316,14 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupMode
return queryset
+@register_filterset
+class RackGroupFilterSet(OrganizationalModelFilterSet):
+
+ class Meta:
+ model = RackGroup
+ fields = ('id', 'name', 'slug', 'description')
+
+
@register_filterset
class RackRoleFilterSet(OrganizationalModelFilterSet):
@@ -417,6 +428,18 @@ class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterS
to_field_name='slug',
label=_('Location (slug)'),
)
+ group_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=RackGroup.objects.all(),
+ distinct=False,
+ label=_('Group (ID)'),
+ )
+ group = django_filters.ModelMultipleChoiceFilter(
+ field_name='group__slug',
+ queryset=RackGroup.objects.all(),
+ distinct=False,
+ to_field_name='slug',
+ label=_('Group (slug)'),
+ )
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack_type__manufacturer',
queryset=Manufacturer.objects.all(),
@@ -551,6 +574,19 @@ class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
to_field_name='slug',
label=_('Location (slug)'),
)
+ group_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=RackGroup.objects.all(),
+ field_name='rack__group',
+ distinct=False,
+ label=_('Group (ID)'),
+ )
+ group = django_filters.ModelMultipleChoiceFilter(
+ field_name='rack__group__slug',
+ queryset=RackGroup.objects.all(),
+ distinct=False,
+ to_field_name='slug',
+ label=_('Group (slug)'),
+ )
status = django_filters.MultipleChoiceFilter(
choices=RackReservationStatusChoices,
distinct=False,
@@ -572,11 +608,30 @@ class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
field_name='units',
lookup_expr='contains'
)
+ unit_count_min = django_filters.NumberFilter(
+ field_name='unit_count',
+ lookup_expr='gte',
+ label=_('Minimum unit count'),
+ )
+ unit_count_max = django_filters.NumberFilter(
+ field_name='unit_count',
+ lookup_expr='lte',
+ label=_('Maximum unit count'),
+ )
class Meta:
model = RackReservation
fields = ('id', 'created', 'description')
+ def filter_queryset(self, queryset):
+ # Annotate unit_count here so unit_count_min/unit_count_max filters can reference it.
+ # When called from the list view the queryset is already annotated; Django silently
+ # overwrites a duplicate annotation with the same expression, so this is safe.
+ queryset = queryset.annotate(
+ unit_count=Func('units', function='CARDINALITY', output_field=IntegerField())
+ )
+ return super().filter_queryset(queryset)
+
def search(self, queryset, name, value):
if not value.strip():
return queryset
@@ -995,7 +1050,7 @@ class ModuleBayTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeCo
class Meta:
model = ModuleBayTemplate
- fields = ('id', 'name', 'label', 'position', 'description')
+ fields = ('id', 'name', 'label', 'position', 'enabled', 'description')
@register_filterset
@@ -1003,7 +1058,7 @@ class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponent
class Meta:
model = DeviceBayTemplate
- fields = ('id', 'name', 'label', 'description')
+ fields = ('id', 'name', 'label', 'enabled', 'description')
@register_filterset
@@ -2373,7 +2428,7 @@ class ModuleBayFilterSet(ModularDeviceComponentFilterSet):
class Meta:
model = ModuleBay
- fields = ('id', 'name', 'label', 'position', 'description')
+ fields = ('id', 'name', 'label', 'position', 'enabled', 'description')
@register_filterset
@@ -2393,7 +2448,7 @@ class DeviceBayFilterSet(DeviceComponentFilterSet):
class Meta:
model = DeviceBay
- fields = ('id', 'name', 'label', 'description')
+ fields = ('id', 'name', 'label', 'enabled', 'description')
@register_filterset
@@ -2546,6 +2601,23 @@ class VirtualChassisFilterSet(PrimaryModelFilterSet):
return queryset.filter(qs_filter).distinct()
+@register_filterset
+class CableBundleFilterSet(PrimaryModelFilterSet):
+
+ class Meta:
+ model = CableBundle
+ fields = ('id', 'name', 'description')
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ return queryset.filter(
+ Q(name__icontains=value) |
+ Q(description__icontains=value) |
+ Q(comments__icontains=value)
+ )
+
+
@register_filterset
class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
termination_a_type = MultiValueContentTypeFilter(
@@ -2566,6 +2638,16 @@ class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
method='_unterminated',
label=_('Unterminated'),
)
+ bundle_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=CableBundle.objects.all(),
+ label=_('Cable bundle (ID)'),
+ )
+ bundle = django_filters.ModelMultipleChoiceFilter(
+ field_name='bundle__name',
+ queryset=CableBundle.objects.all(),
+ to_field_name='name',
+ label=_('Cable bundle (name)'),
+ )
type = django_filters.MultipleChoiceFilter(
choices=CableTypeChoices,
distinct=False,
diff --git a/netbox/dcim/forms/bulk_create.py b/netbox/dcim/forms/bulk_create.py
index a95a80f63..e5546e08f 100644
--- a/netbox/dcim/forms/bulk_create.py
+++ b/netbox/dcim/forms/bulk_create.py
@@ -3,9 +3,10 @@ from django.utils.translation import gettext_lazy as _
from dcim.models import *
from extras.models import Tag
-from netbox.forms.mixins import CustomFieldsMixin
+from netbox.forms.mixins import ChangelogMessageMixin, CustomFieldsMixin
from utilities.forms import form_from_model
from utilities.forms.fields import DynamicModelMultipleChoiceField, ExpandableNameField
+from utilities.forms.mixins import BackgroundJobMixin
from .object_create import ComponentCreateForm
@@ -27,7 +28,7 @@ __all__ = (
# Device components
#
-class DeviceBulkAddComponentForm(CustomFieldsMixin, ComponentCreateForm):
+class DeviceBulkAddComponentForm(BackgroundJobMixin, ChangelogMessageMixin, CustomFieldsMixin, ComponentCreateForm):
pk = forms.ModelMultipleChoiceField(
queryset=Device.objects.all(),
widget=forms.MultipleHiddenInput()
@@ -108,10 +109,13 @@ class RearPortBulkCreateForm(
field_order = ('name', 'label', 'type', 'positions', 'mark_connected', 'description', 'tags')
-class ModuleBayBulkCreateForm(DeviceBulkAddComponentForm):
+class ModuleBayBulkCreateForm(
+ form_from_model(ModuleBay, ['enabled']),
+ DeviceBulkAddComponentForm
+):
model = ModuleBay
- field_order = ('name', 'label', 'position', 'description', 'tags')
- replication_fields = ('name', 'label', 'position')
+ field_order = ('name', 'label', 'position', 'enabled', 'description', 'tags')
+ replication_fields = ('name', 'label', 'position', 'enabled')
position = ExpandableNameField(
label=_('Position'),
required=False,
@@ -119,9 +123,12 @@ class ModuleBayBulkCreateForm(DeviceBulkAddComponentForm):
)
-class DeviceBayBulkCreateForm(DeviceBulkAddComponentForm):
+class DeviceBayBulkCreateForm(
+ form_from_model(DeviceBay, ['enabled']),
+ DeviceBulkAddComponentForm
+):
model = DeviceBay
- field_order = ('name', 'label', 'description', 'tags')
+ field_order = ('name', 'label', 'enabled', 'description', 'tags')
class InventoryItemBulkCreateForm(
diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py
index 1282639fb..d9ef8600f 100644
--- a/netbox/dcim/forms/bulk_edit.py
+++ b/netbox/dcim/forms/bulk_edit.py
@@ -35,6 +35,7 @@ from wireless.models import WirelessLAN, WirelessLANGroup
__all__ = (
'CableBulkEditForm',
+ 'CableBundleBulkEditForm',
'ConsolePortBulkEditForm',
'ConsolePortTemplateBulkEditForm',
'ConsoleServerPortBulkEditForm',
@@ -67,6 +68,7 @@ __all__ = (
'PowerPortBulkEditForm',
'PowerPortTemplateBulkEditForm',
'RackBulkEditForm',
+ 'RackGroupBulkEditForm',
'RackReservationBulkEditForm',
'RackRoleBulkEditForm',
'RackTypeBulkEditForm',
@@ -207,6 +209,14 @@ class LocationBulkEditForm(NestedGroupModelBulkEditForm):
nullable_fields = ('parent', 'tenant', 'facility', 'description', 'comments')
+class RackGroupBulkEditForm(OrganizationalModelBulkEditForm):
+ model = RackGroup
+ fieldsets = (
+ FieldSet('description'),
+ )
+ nullable_fields = ('description', 'comments')
+
+
class RackRoleBulkEditForm(OrganizationalModelBulkEditForm):
color = ColorField(
label=_('Color'),
@@ -342,6 +352,11 @@ class RackBulkEditForm(PrimaryModelBulkEditForm):
'site_id': '$site'
}
)
+ group = DynamicModelChoiceField(
+ label=_('Group'),
+ queryset=RackGroup.objects.all(),
+ required=False
+ )
tenant = DynamicModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
@@ -441,14 +456,16 @@ class RackBulkEditForm(PrimaryModelBulkEditForm):
model = Rack
fieldsets = (
- FieldSet('status', 'role', 'tenant', 'serial', 'asset_tag', 'rack_type', 'description', name=_('Rack')),
+ FieldSet(
+ 'status', 'group', 'role', 'tenant', 'serial', 'asset_tag', 'rack_type', 'description', name=_('Rack')
+ ),
FieldSet('region', 'site_group', 'site', 'location', name=_('Location')),
FieldSet('outer_width', 'outer_height', 'outer_depth', 'outer_unit', name=_('Outer Dimensions')),
FieldSet('form_factor', 'width', 'u_height', 'desc_units', 'airflow', 'mounting_depth', name=_('Hardware')),
FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')),
)
nullable_fields = (
- 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_height', 'outer_depth',
+ 'location', 'group', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_height', 'outer_depth',
'outer_unit', 'weight', 'max_weight', 'weight_unit', 'description', 'comments',
)
@@ -776,6 +793,24 @@ class ModuleBulkEditForm(PrimaryModelBulkEditForm):
nullable_fields = ('serial', 'description', 'comments')
+class CableBundleBulkEditForm(PrimaryModelBulkEditForm):
+ pk = forms.ModelMultipleChoiceField(
+ queryset=CableBundle.objects.all(),
+ widget=forms.MultipleHiddenInput
+ )
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False,
+ )
+
+ model = CableBundle
+ fieldsets = (
+ FieldSet('description',),
+ )
+ nullable_fields = ('description', 'comments')
+
+
class CableBulkEditForm(PrimaryModelBulkEditForm):
type = forms.ChoiceField(
label=_('Type'),
@@ -800,6 +835,11 @@ class CableBulkEditForm(PrimaryModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
+ bundle = DynamicModelChoiceField(
+ label=_('Bundle'),
+ queryset=CableBundle.objects.all(),
+ required=False,
+ )
label = forms.CharField(
label=_('Label'),
max_length=100,
@@ -823,11 +863,11 @@ class CableBulkEditForm(PrimaryModelBulkEditForm):
model = Cable
fieldsets = (
- FieldSet('type', 'status', 'profile', 'tenant', 'label', 'description'),
+ FieldSet('type', 'status', 'profile', 'tenant', 'bundle', 'label', 'description'),
FieldSet('color', 'length', 'length_unit', name=_('Attributes')),
)
nullable_fields = (
- 'type', 'status', 'profile', 'tenant', 'label', 'color', 'length', 'description', 'comments',
+ 'type', 'status', 'profile', 'tenant', 'bundle', 'label', 'color', 'length', 'description', 'comments',
)
@@ -1211,6 +1251,11 @@ class ModuleBayTemplateBulkEditForm(ComponentTemplateBulkEditForm):
label=_('Description'),
required=False
)
+ enabled = forms.NullBooleanField(
+ label=_('Enabled'),
+ required=False,
+ widget=BulkEditNullBooleanSelect,
+ )
nullable_fields = ('label', 'position', 'description')
@@ -1229,6 +1274,11 @@ class DeviceBayTemplateBulkEditForm(ComponentTemplateBulkEditForm):
label=_('Description'),
required=False
)
+ enabled = forms.NullBooleanField(
+ label=_('Enabled'),
+ required=False,
+ widget=BulkEditNullBooleanSelect,
+ )
nullable_fields = ('label', 'description')
@@ -1653,23 +1703,23 @@ class RearPortBulkEditForm(
class ModuleBayBulkEditForm(
- form_from_model(ModuleBay, ['label', 'position', 'description']),
+ form_from_model(ModuleBay, ['label', 'position', 'enabled', 'description']),
NetBoxModelBulkEditForm
):
model = ModuleBay
fieldsets = (
- FieldSet('label', 'position', 'description'),
+ FieldSet('label', 'position', 'enabled', 'description'),
)
nullable_fields = ('label', 'position', 'description')
class DeviceBayBulkEditForm(
- form_from_model(DeviceBay, ['label', 'description']),
+ form_from_model(DeviceBay, ['label', 'enabled', 'description']),
NetBoxModelBulkEditForm
):
model = DeviceBay
fieldsets = (
- FieldSet('label', 'description'),
+ FieldSet('label', 'enabled', 'description'),
)
nullable_fields = ('label', 'description')
diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py
index 1cfc58983..6f3197919 100644
--- a/netbox/dcim/forms/bulk_import.py
+++ b/netbox/dcim/forms/bulk_import.py
@@ -34,6 +34,7 @@ from wireless.choices import WirelessRoleChoices
from .common import ModuleCommonForm
__all__ = (
+ 'CableBundleImportForm',
'CableImportForm',
'ConsolePortImportForm',
'ConsoleServerPortImportForm',
@@ -57,6 +58,7 @@ __all__ = (
'PowerOutletImportForm',
'PowerPanelImportForm',
'PowerPortImportForm',
+ 'RackGroupImportForm',
'RackImportForm',
'RackReservationImportForm',
'RackRoleImportForm',
@@ -187,6 +189,13 @@ class LocationImportForm(NestedGroupModelImportForm):
self.fields['parent'].queryset = self.fields['parent'].queryset.filter(**params)
+class RackGroupImportForm(OrganizationalModelImportForm):
+
+ class Meta:
+ model = RackGroup
+ fields = ('name', 'slug', 'description', 'owner', 'comments', 'tags')
+
+
class RackRoleImportForm(OrganizationalModelImportForm):
class Meta:
@@ -261,6 +270,13 @@ class RackImportForm(PrimaryModelImportForm):
to_field_name='name',
help_text=_('Name of assigned tenant')
)
+ group = CSVModelChoiceField(
+ label=_('Rack group'),
+ queryset=RackGroup.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text=_('Name of assigned group')
+ )
status = CSVChoiceField(
label=_('Status'),
choices=RackStatusChoices,
@@ -318,10 +334,10 @@ class RackImportForm(PrimaryModelImportForm):
class Meta:
model = Rack
fields = (
- 'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor', 'serial',
- 'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit',
- 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner', 'comments',
- 'tags',
+ 'site', 'location', 'group', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor',
+ 'serial', 'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth',
+ 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'owner',
+ 'comments', 'tags',
)
def __init__(self, data=None, *args, **kwargs):
@@ -1138,7 +1154,13 @@ class ModuleBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = ModuleBay
- fields = ('device', 'name', 'label', 'position', 'description', 'owner', 'tags')
+ fields = ('device', 'name', 'label', 'position', 'enabled', 'description', 'owner', 'tags')
+
+ def clean_enabled(self):
+ # Make sure enabled is True when it's not included in the uploaded data
+ if 'enabled' not in self.data:
+ return True
+ return self.cleaned_data['enabled']
class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
@@ -1160,7 +1182,7 @@ class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
class Meta:
model = DeviceBay
- fields = ('device', 'name', 'label', 'installed_device', 'description', 'owner', 'tags')
+ fields = ('device', 'name', 'label', 'enabled', 'installed_device', 'description', 'owner', 'tags')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -1188,6 +1210,12 @@ class DeviceBayImportForm(OwnerCSVMixin, NetBoxModelImportForm):
else:
self.fields['installed_device'].queryset = Device.objects.none()
+ def clean_enabled(self):
+ # Make sure enabled is True when it's not included in the uploaded data
+ if 'enabled' not in self.data:
+ return True
+ return self.cleaned_data['enabled']
+
class InventoryItemImportForm(OwnerCSVMixin, NetBoxModelImportForm):
device = CSVModelChoiceField(
@@ -1397,6 +1425,12 @@ class MACAddressImportForm(PrimaryModelImportForm):
# Cables
#
+class CableBundleImportForm(PrimaryModelImportForm):
+ class Meta:
+ model = CableBundle
+ fields = ('name', 'description', 'owner', 'comments', 'tags')
+
+
class CableImportForm(PrimaryModelImportForm):
# Termination A
side_a_site = CSVModelChoiceField(
@@ -1490,6 +1524,13 @@ class CableImportForm(PrimaryModelImportForm):
to_field_name='name',
help_text=_('Assigned tenant')
)
+ bundle = CSVModelChoiceField(
+ label=_('Bundle'),
+ queryset=CableBundle.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text=_('Cable bundle name'),
+ )
length_unit = CSVChoiceField(
label=_('Length unit'),
choices=CableLengthUnitChoices,
@@ -1508,7 +1549,7 @@ class CableImportForm(PrimaryModelImportForm):
fields = [
'side_a_site', 'side_a_device', 'side_a_power_panel', 'side_a_type', 'side_a_name',
'side_b_site', 'side_b_device', 'side_b_power_panel', 'side_b_type', 'side_b_name',
- 'type', 'status', 'profile', 'tenant', 'label', 'color', 'length', 'length_unit',
+ 'type', 'status', 'profile', 'tenant', 'bundle', 'label', 'color', 'length', 'length_unit',
'description', 'owner', 'comments', 'tags',
]
diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py
index 162fd6213..f3df113b4 100644
--- a/netbox/dcim/forms/common.py
+++ b/netbox/dcim/forms/common.py
@@ -113,7 +113,6 @@ class ModuleCommonForm(forms.Form):
raise forms.ValidationError(
_("Cannot install module with placeholder values in a module bay with no position defined.")
)
-
try:
resolved_name = resolve_module_placeholder(template.name, positions)
except ValueError as e:
diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py
index 82ee655b8..9759a5df4 100644
--- a/netbox/dcim/forms/filtersets.py
+++ b/netbox/dcim/forms/filtersets.py
@@ -27,6 +27,7 @@ from vpn.models import L2VPN
from wireless.choices import *
__all__ = (
+ 'CableBundleFilterForm',
'CableFilterForm',
'ConsoleConnectionFilterForm',
'ConsolePortFilterForm',
@@ -64,6 +65,7 @@ __all__ = (
'PowerPortTemplateFilterForm',
'RackElevationFilterForm',
'RackFilterForm',
+ 'RackGroupFilterForm',
'RackReservationFilterForm',
'RackRoleFilterForm',
'RackTypeFilterForm',
@@ -276,6 +278,15 @@ class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, NestedGroupM
tag = TagFilterField(model)
+class RackGroupFilterForm(OrganizationalModelFilterSetForm):
+ model = RackGroup
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
+ )
+ tag = TagFilterField(model)
+
+
class RackRoleFilterForm(OrganizationalModelFilterSetForm):
model = RackRole
fieldsets = (
@@ -355,7 +366,7 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterFo
model = Rack
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')),
+ FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'group_id', name=_('Location')),
FieldSet('status', 'role_id', 'manufacturer_id', 'rack_type_id', 'serial', 'asset_tag', name=_('Rack')),
FieldSet('form_factor', 'width', 'u_height', 'airflow', name=_('Hardware')),
FieldSet('starting_unit', 'desc_units', name=_('Numbering')),
@@ -392,6 +403,12 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterFo
},
label=_('Location')
)
+ group_id = DynamicModelMultipleChoiceField(
+ queryset=RackGroup.objects.all(),
+ required=False,
+ null_option='None',
+ label=_('Rack group')
+ )
status = forms.MultipleChoiceField(
label=_('Status'),
choices=RackStatusChoices,
@@ -435,7 +452,7 @@ class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, RackBaseFilterFo
class RackElevationFilterForm(RackFilterForm):
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'id', name=_('Location')),
+ FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'group_id', 'id', name=_('Location')),
FieldSet('status', 'role_id', name=_('Function')),
FieldSet('type', 'width', 'serial', 'asset_tag', name=_('Hardware')),
FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')),
@@ -458,8 +475,8 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = RackReservation
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('status', 'user_id', name=_('Reservation')),
- FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Rack')),
+ FieldSet('status', 'user_id', 'unit_count_min', 'unit_count_max', name=_('Reservation')),
+ FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'group_id', 'rack_id', name=_('Rack')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
@@ -491,10 +508,17 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
label=_('Location'),
null_option='None'
)
+ group_id = DynamicModelMultipleChoiceField(
+ queryset=RackGroup.objects.all(),
+ required=False,
+ null_option='None',
+ label=_('Rack group')
+ )
rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(),
required=False,
query_params={
+ 'group_id': '$group_id',
'site_id': '$site_id',
'location_id': '$location_id',
},
@@ -510,6 +534,14 @@ class RackReservationFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
required=False,
label=_('User')
)
+ unit_count_min = forms.IntegerField(
+ required=False,
+ label=_("Minimum U's")
+ )
+ unit_count_max = forms.IntegerField(
+ required=False,
+ label=_("Maximum U's")
+ )
tag = TagFilterField(model)
@@ -1161,12 +1193,24 @@ class VirtualChassisFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
tag = TagFilterField(model)
+class CableBundleFilterForm(PrimaryModelFilterSetForm):
+ model = CableBundle
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('name', name=_('Attributes')),
+ )
+ tag = TagFilterField(model)
+
+
class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
model = Cable
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('site_id', 'location_id', 'rack_id', 'device_id', name=_('Location')),
- FieldSet('type', 'status', 'profile', 'color', 'length', 'length_unit', 'unterminated', name=_('Attributes')),
+ FieldSet(
+ 'type', 'status', 'profile', 'color', 'length', 'length_unit', 'unterminated', 'bundle_id',
+ name=_('Attributes'),
+ ),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
@@ -1248,6 +1292,11 @@ class CableFilterForm(TenancyFilterForm, PrimaryModelFilterSetForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
+ bundle_id = DynamicModelMultipleChoiceField(
+ queryset=CableBundle.objects.all(),
+ required=False,
+ label=_('Bundle'),
+ )
tag = TagFilterField(model)
@@ -1841,7 +1890,7 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
model = ModuleBay
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('name', 'label', 'position', name=_('Attributes')),
+ FieldSet('name', 'label', 'position', 'enabled', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
'tenant_id', 'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
@@ -1849,31 +1898,41 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
- tag = TagFilterField(model)
position = forms.CharField(
label=_('Position'),
required=False
)
+ enabled = forms.NullBooleanField(
+ label=_('Enabled'),
+ required=False,
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
+ )
+ tag = TagFilterField(model)
class ModuleBayTemplateFilterForm(ModularDeviceComponentTemplateFilterForm):
model = ModuleBayTemplate
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('name', 'label', 'position', name=_('Attributes')),
+ FieldSet('name', 'label', 'position', 'enabled', name=_('Attributes')),
FieldSet('device_type_id', 'module_type_id', name=_('Device')),
)
position = forms.CharField(
label=_('Position'),
required=False,
)
+ enabled = forms.NullBooleanField(
+ label=_('Enabled'),
+ required=False,
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
+ )
class DeviceBayFilterForm(DeviceComponentFilterForm):
model = DeviceBay
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('name', 'label', name=_('Attributes')),
+ FieldSet('name', 'label', 'enabled', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')),
FieldSet(
'tenant_id', 'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id',
@@ -1881,6 +1940,11 @@ class DeviceBayFilterForm(DeviceComponentFilterForm):
),
FieldSet('owner_group_id', 'owner_id', name=_('Ownership')),
)
+ enabled = forms.NullBooleanField(
+ label=_('Enabled'),
+ required=False,
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
+ )
tag = TagFilterField(model)
@@ -1888,9 +1952,14 @@ class DeviceBayTemplateFilterForm(DeviceComponentTemplateFilterForm):
model = DeviceBayTemplate
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
- FieldSet('name', 'label', name=_('Attributes')),
+ FieldSet('name', 'label', 'enabled', name=_('Attributes')),
FieldSet('device_type_id', name=_('Device')),
)
+ enabled = forms.NullBooleanField(
+ label=_('Enabled'),
+ required=False,
+ widget=forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
+ )
class InventoryItemFilterForm(DeviceComponentFilterForm):
diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py
index 9f3a6de63..9672cc54b 100644
--- a/netbox/dcim/forms/model_forms.py
+++ b/netbox/dcim/forms/model_forms.py
@@ -39,6 +39,7 @@ from wireless.models import WirelessLAN, WirelessLANGroup
from .common import InterfaceCommonForm, ModuleCommonForm
__all__ = (
+ 'CableBundleForm',
'CableForm',
'ConsolePortForm',
'ConsolePortTemplateForm',
@@ -74,6 +75,7 @@ __all__ = (
'PowerPortForm',
'PowerPortTemplateForm',
'RackForm',
+ 'RackGroupForm',
'RackReservationForm',
'RackRoleForm',
'RackTypeForm',
@@ -232,6 +234,18 @@ class LocationForm(TenancyForm, NestedGroupModelForm):
)
+class RackGroupForm(OrganizationalModelForm):
+ fieldsets = (
+ FieldSet('name', 'slug', 'description', 'tags', name=_('Rack Group')),
+ )
+
+ class Meta:
+ model = RackGroup
+ fields = [
+ 'name', 'slug', 'description', 'owner', 'comments', 'tags',
+ ]
+
+
class RackRoleForm(OrganizationalModelForm):
fieldsets = (
FieldSet('name', 'slug', 'color', 'description', 'tags', name=_('Rack Role')),
@@ -289,6 +303,11 @@ class RackForm(TenancyForm, PrimaryModelForm):
'site_id': '$site'
}
)
+ group = DynamicModelChoiceField(
+ label=_('Rack Group'),
+ queryset=RackGroup.objects.all(),
+ required=False
+ )
role = DynamicModelChoiceField(
label=_('Role'),
queryset=RackRole.objects.all(),
@@ -304,7 +323,7 @@ class RackForm(TenancyForm, PrimaryModelForm):
fieldsets = (
FieldSet(
- 'site', 'location', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags',
+ 'site', 'location', 'group', 'name', 'status', 'role', 'rack_type', 'description', 'airflow', 'tags',
name=_('Rack')
),
FieldSet('facility_id', 'serial', 'asset_tag', name=_('Inventory Control')),
@@ -314,7 +333,7 @@ class RackForm(TenancyForm, PrimaryModelForm):
class Meta:
model = Rack
fields = [
- 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
+ 'site', 'location', 'group', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight',
'weight_unit', 'description', 'owner', 'comments', 'tags',
@@ -784,7 +803,7 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm):
'device_id': '$device',
},
context={
- 'disabled': 'installed_module',
+ 'disabled': '_occupied',
},
)
module_type = DynamicModelChoiceField(
@@ -838,6 +857,17 @@ def get_termination_type_choices():
])
+class CableBundleForm(PrimaryModelForm):
+
+ fieldsets = (
+ FieldSet('name', 'description', 'tags', name=_('Cable Bundle')),
+ )
+
+ class Meta:
+ model = CableBundle
+ fields = ['name', 'description', 'owner', 'comments', 'tags']
+
+
class CableForm(TenancyForm, PrimaryModelForm):
a_terminations_type = forms.ChoiceField(
choices=get_termination_type_choices,
@@ -851,12 +881,17 @@ class CableForm(TenancyForm, PrimaryModelForm):
widget=HTMXSelect(),
label=_('Type')
)
+ bundle = DynamicModelChoiceField(
+ queryset=CableBundle.objects.all(),
+ required=False,
+ label=_('Bundle'),
+ )
class Meta:
model = Cable
fields = [
'a_terminations_type', 'b_terminations_type', 'type', 'status', 'profile', 'tenant_group', 'tenant',
- 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
+ 'bundle', 'label', 'color', 'length', 'length_unit', 'description', 'owner', 'comments', 'tags',
]
@@ -1063,7 +1098,9 @@ class ModularComponentTemplateForm(ComponentTemplateForm):
self.fields['name'].help_text = _(
"Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range are not "
"supported (example: [ge,xe]-0/0/[0-9]). The token {module}, if present, will be "
- "automatically replaced with the position value when creating a new module."
+ "automatically replaced with the position value when creating a new module. "
+ "The token {vc_position} will be replaced with the device's Virtual Chassis position "
+ "(use {vc_position:1} to specify a fallback (default is 0))"
)
@@ -1224,26 +1261,26 @@ class ModuleBayTemplateForm(ModularComponentTemplateForm):
FieldSet('device_type', name=_('Device Type')),
FieldSet('module_type', name=_('Module Type')),
),
- 'name', 'label', 'position', 'description',
+ 'name', 'label', 'position', 'enabled', 'description',
),
)
class Meta:
model = ModuleBayTemplate
fields = [
- 'device_type', 'module_type', 'name', 'label', 'position', 'description',
+ 'device_type', 'module_type', 'name', 'label', 'position', 'enabled', 'description',
]
class DeviceBayTemplateForm(ComponentTemplateForm):
fieldsets = (
- FieldSet('device_type', 'name', 'label', 'description'),
+ FieldSet('device_type', 'name', 'label', 'enabled', 'description'),
)
class Meta:
model = DeviceBayTemplate
fields = [
- 'device_type', 'name', 'label', 'description',
+ 'device_type', 'name', 'label', 'enabled', 'description',
]
@@ -1689,25 +1726,25 @@ class RearPortForm(ModularDeviceComponentForm):
class ModuleBayForm(ModularDeviceComponentForm):
fieldsets = (
- FieldSet('device', 'module', 'name', 'label', 'position', 'description', 'tags',),
+ FieldSet('device', 'module', 'name', 'label', 'position', 'enabled', 'description', 'tags',),
)
class Meta:
model = ModuleBay
fields = [
- 'device', 'module', 'name', 'label', 'position', 'description', 'owner', 'tags',
+ 'device', 'module', 'name', 'label', 'position', 'enabled', 'description', 'owner', 'tags',
]
class DeviceBayForm(DeviceComponentForm):
fieldsets = (
- FieldSet('device', 'name', 'label', 'description', 'tags',),
+ FieldSet('device', 'name', 'label', 'enabled', 'description', 'tags',),
)
class Meta:
model = DeviceBay
fields = [
- 'device', 'name', 'label', 'description', 'owner', 'tags',
+ 'device', 'name', 'label', 'enabled', 'description', 'owner', 'tags',
]
diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py
index c5aa04236..02fb3ef64 100644
--- a/netbox/dcim/graphql/filters.py
+++ b/netbox/dcim/graphql/filters.py
@@ -63,6 +63,7 @@ if TYPE_CHECKING:
from .enums import *
__all__ = (
+ 'CableBundleFilter',
'CableFilter',
'CableTerminationFilter',
'ConsolePortFilter',
@@ -99,6 +100,7 @@ __all__ = (
'PowerPortFilter',
'PowerPortTemplateFilter',
'RackFilter',
+ 'RackGroupFilter',
'RackReservationFilter',
'RackRoleFilter',
'RackTypeFilter',
@@ -112,6 +114,11 @@ __all__ = (
)
+@strawberry_django.filter_type(models.CableBundle, lookups=True)
+class CableBundleFilter(PrimaryModelFilter):
+ name: StrFilterLookup[str] | None = strawberry_django.filter_field()
+
+
@strawberry_django.filter_type(models.Cable, lookups=True)
class CableFilter(TenancyFilterMixin, PrimaryModelFilter):
type: BaseFilterLookup[Annotated['CableTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
@@ -317,6 +324,7 @@ class DeviceFilter(
@strawberry_django.filter_type(models.DeviceBay, lookups=True)
class DeviceBayFilter(ComponentModelFilterMixin, NetBoxModelFilter):
+ enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
installed_device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
strawberry_django.filter_field()
)
@@ -325,7 +333,7 @@ class DeviceBayFilter(ComponentModelFilterMixin, NetBoxModelFilter):
@strawberry_django.filter_type(models.DeviceBayTemplate, lookups=True)
class DeviceBayTemplateFilter(ComponentTemplateFilterMixin, ChangeLoggedModelFilter):
- pass
+ enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
@strawberry_django.filter_type(models.InventoryItemTemplate, lookups=True)
@@ -741,11 +749,13 @@ class ModuleBayFilter(ModularComponentFilterMixin, NetBoxModelFilter):
)
parent_id: ID | None = strawberry_django.filter_field()
position: StrFilterLookup[str] | None = strawberry_django.filter_field()
+ enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
@strawberry_django.filter_type(models.ModuleBayTemplate, lookups=True)
class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
position: StrFilterLookup[str] | None = strawberry_django.filter_field()
+ enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
@strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True)
@@ -962,6 +972,10 @@ class RackFilter(
location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
+ group: Annotated['RackGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
+ strawberry_django.filter_field()
+ )
+ group_id: ID | None = strawberry_django.filter_field()
status: BaseFilterLookup[Annotated['RackStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
@@ -977,6 +991,11 @@ class RackFilter(
)
+@strawberry_django.filter_type(models.RackGroup, lookups=True)
+class RackGroupFilter(OrganizationalModelFilter):
+ pass
+
+
@strawberry_django.filter_type(models.RackReservation, lookups=True)
class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter):
rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
@@ -984,6 +1003,7 @@ class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter):
units: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
+ unit_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field()
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
user_id: ID | None = strawberry_django.filter_field()
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
diff --git a/netbox/dcim/graphql/schema.py b/netbox/dcim/graphql/schema.py
index fcf1e828f..710254120 100644
--- a/netbox/dcim/graphql/schema.py
+++ b/netbox/dcim/graphql/schema.py
@@ -9,6 +9,9 @@ class DCIMQuery:
cable: CableType = strawberry_django.field()
cable_list: list[CableType] = strawberry_django.field()
+ cable_bundle: CableBundleType = strawberry_django.field()
+ cable_bundle_list: list[CableBundleType] = strawberry_django.field()
+
console_port: ConsolePortType = strawberry_django.field()
console_port_list: list[ConsolePortType] = strawberry_django.field()
@@ -102,6 +105,9 @@ class DCIMQuery:
power_port_template: PowerPortTemplateType = strawberry_django.field()
power_port_template_list: list[PowerPortTemplateType] = strawberry_django.field()
+ rack_group: RackGroupType = strawberry_django.field()
+ rack_group_list: list[RackGroupType] = strawberry_django.field()
+
rack_type: RackTypeType = strawberry_django.field()
rack_type_list: list[RackTypeType] = strawberry_django.field()
diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py
index 2a6c3750a..6cbab28f2 100644
--- a/netbox/dcim/graphql/types.py
+++ b/netbox/dcim/graphql/types.py
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Annotated
import strawberry
import strawberry_django
+from django.db.models import Func, IntegerField
from core.graphql.mixins import ChangelogMixin
from dcim import models
@@ -39,6 +40,7 @@ if TYPE_CHECKING:
from wireless.graphql.types import WirelessLANType, WirelessLinkType
__all__ = (
+ 'CableBundleType',
'CableType',
'ComponentType',
'ConsolePortTemplateType',
@@ -73,6 +75,7 @@ __all__ = (
'PowerPanelType',
'PowerPortTemplateType',
'PowerPortType',
+ 'RackGroupType',
'RackReservationType',
'RackRoleType',
'RackType',
@@ -126,6 +129,16 @@ class ModularComponentTemplateType(ComponentTemplateType):
#
+@strawberry_django.type(
+ models.CableBundle,
+ fields='__all__',
+ filters=CableBundleFilter,
+ pagination=True
+)
+class CableBundleType(PrimaryObjectType):
+ cables: list[Annotated['CableType', strawberry.lazy('dcim.graphql.types')]]
+
+
@strawberry_django.type(
models.CableTermination,
exclude=['termination_type', 'termination_id', '_device', '_rack', '_location', '_site'],
@@ -157,6 +170,7 @@ class CableTerminationType(NetBoxObjectType):
class CableType(PrimaryObjectType):
color: str
tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None
+ bundle: Annotated['CableBundleType', strawberry.lazy('dcim.graphql.types')] | None
terminations: list[CableTerminationType]
@@ -737,6 +751,17 @@ class PowerPortTemplateType(ModularComponentTemplateType):
poweroutlet_templates: list[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
+@strawberry_django.type(
+ models.RackGroup,
+ fields='__all__',
+ filters=RackGroupFilter,
+ pagination=True
+)
+class RackGroupType(OrganizationalObjectType):
+
+ racks: list[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
+
+
@strawberry_django.type(
models.RackType,
fields='__all__',
@@ -757,6 +782,7 @@ class RackTypeType(ImageAttachmentsMixin, PrimaryObjectType):
class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType):
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
+ group: Annotated["RackGroupType", strawberry.lazy('dcim.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
role: Annotated["RackRoleType", strawberry.lazy('dcim.graphql.types')] | None
@@ -779,6 +805,17 @@ class RackReservationType(PrimaryObjectType):
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
user: Annotated["UserType", strawberry.lazy('users.graphql.types')]
+ @classmethod
+ def get_queryset(cls, queryset, info, **kwargs):
+ queryset = super().get_queryset(queryset, info, **kwargs)
+ return queryset.annotate(
+ unit_count=Func('units', function='CARDINALITY', output_field=IntegerField())
+ )
+
+ @strawberry.field
+ def unit_count(self) -> int:
+ return len(self.units)
+
@strawberry_django.type(
models.RackRole,
diff --git a/netbox/dcim/migrations/0206_load_module_type_profiles.py b/netbox/dcim/migrations/0206_load_module_type_profiles.py
index 87661a8ac..1b9873faf 100644
--- a/netbox/dcim/migrations/0206_load_module_type_profiles.py
+++ b/netbox/dcim/migrations/0206_load_module_type_profiles.py
@@ -22,17 +22,21 @@ def load_initial_data(apps, schema_editor):
'power_supply',
'expansion_card'
)
+ profile_objects = []
for name in initial_profiles:
file_path = DATA_FILES_PATH / f'{name}.json'
with file_path.open('r') as f:
data = json.load(f)
try:
- ModuleTypeProfile.objects.using(db_alias).create(**data)
+ profile = ModuleTypeProfile(**data)
+ profile_objects.append(profile)
except Exception as e:
print(f"Error loading data from {file_path}")
raise e
+ ModuleTypeProfile.objects.using(db_alias).bulk_create(profile_objects)
+
class Migration(migrations.Migration):
diff --git a/netbox/dcim/migrations/0228_rack_group.py b/netbox/dcim/migrations/0228_rack_group.py
new file mode 100644
index 000000000..27ffd97d7
--- /dev/null
+++ b/netbox/dcim/migrations/0228_rack_group.py
@@ -0,0 +1,109 @@
+import django.db.models.deletion
+import taggit.managers
+from django.db import migrations, models
+
+import netbox.models.deletion
+import utilities.json
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('dcim', '0227_alter_interface_speed_bigint'),
+ ('extras', '0134_owner'),
+ ('users', '0015_owner'),
+ ]
+
+ operations = [
+ # Rename legacy database objects left over from when the RackGroup model was renamed
+ # to Location (v2.11) and Rack.group was renamed to Rack.location. Old installations
+ # retained the original names, which conflict with the new dcim_rackgroup table and
+ # dcim_rack.group_id column created by this migration. No-op on fresh installs.
+ migrations.RunSQL(
+ sql=[
+ "ALTER INDEX IF EXISTS dcim_rackgroup_pkey RENAME TO dcim_location_pkey",
+ "ALTER INDEX IF EXISTS dcim_rackgroup_parent_id_cc315105 RENAME TO dcim_location_parent_id_d77f3318",
+ "ALTER INDEX IF EXISTS dcim_rackgroup_site_id_13520e89 RENAME TO dcim_location_site_id_b55e975f",
+ "ALTER INDEX IF EXISTS dcim_rackgroup_slug_3f4582a7 RENAME TO dcim_location_slug_352c5472",
+ "ALTER INDEX IF EXISTS dcim_rackgroup_slug_3f4582a7_like RENAME TO dcim_location_slug_352c5472_like",
+ "ALTER INDEX IF EXISTS dcim_rackgroup_tree_id_9c2ad6f4 RENAME TO dcim_location_tree_id_5089ef14",
+ "ALTER SEQUENCE IF EXISTS dcim_rackgroup_id_seq RENAME TO dcim_location_id_seq",
+ # Rename the legacy index on dcim_rack from when Rack.group was renamed to
+ # Rack.location. The column was renamed but the index was not, so it still
+ # carries the old "group_id" name while indexing location_id. Its name
+ # collides with the new index Django creates for the new Rack.group FK.
+ "ALTER INDEX IF EXISTS dcim_rack_group_id_44e90ea9 RENAME TO dcim_rack_location_id_5f63ec31",
+ ],
+ reverse_sql=migrations.RunSQL.noop,
+ ),
+ # PostgreSQL does not support IF EXISTS on RENAME CONSTRAINT, so use a DO block.
+ # Target names match what a fresh v4.5.9 install produces (Django generates the FK
+ # constraint name as