diff --git a/.github/ISSUE_TEMPLATE/01-feature_request.yaml b/.github/ISSUE_TEMPLATE/01-feature_request.yaml index 59a95a5cd..d4e4c4c67 100644 --- a/.github/ISSUE_TEMPLATE/01-feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/01-feature_request.yaml @@ -15,7 +15,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v4.6.2 + placeholder: v4.6.3 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/02-bug_report.yaml b/.github/ISSUE_TEMPLATE/02-bug_report.yaml index c222ea3d2..da580c483 100644 --- a/.github/ISSUE_TEMPLATE/02-bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/02-bug_report.yaml @@ -27,7 +27,7 @@ body: attributes: label: NetBox Version description: What version of NetBox are you currently running? - placeholder: v4.6.2 + placeholder: v4.6.3 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/03-performance.yaml b/.github/ISSUE_TEMPLATE/03-performance.yaml index ca6cec3de..8d96543c8 100644 --- a/.github/ISSUE_TEMPLATE/03-performance.yaml +++ b/.github/ISSUE_TEMPLATE/03-performance.yaml @@ -8,7 +8,7 @@ body: attributes: label: NetBox Version description: What version of NetBox are you currently running? - placeholder: v4.6.2 + placeholder: v4.6.3 validations: required: true - type: dropdown diff --git a/.github/workflows/no-blank-issue.yml b/.github/workflows/no-blank-issue.yml new file mode 100644 index 000000000..0cb5a8f84 --- /dev/null +++ b/.github/workflows/no-blank-issue.yml @@ -0,0 +1,21 @@ +name: Enforce issue templates + +on: + issues: + types: + - opened + - reopened + +permissions: + issues: write + +jobs: + no-blank-issue: + name: No Blank Issue + runs-on: ubuntu-slim + + steps: + - name: Close new issues without labels + uses: ldez/no-blank-issue@800e2d0c81c9e0ca7bdb58f3e7480a74602d91e0 # v1.2.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/base_requirements.txt b/base_requirements.txt index da7695f5a..c351079bd 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -141,6 +141,11 @@ psycopg[c,pool] # https://github.com/yaml/pyyaml/blob/master/CHANGES PyYAML +# redis-py +# https://github.com/redis/redis-py +# Default protocol changes to RESP3 in v8.0; see #22388 +redis<8.0 + # Requests # https://github.com/psf/requests/blob/main/HISTORY.md requests diff --git a/contrib/generated_schema.json b/contrib/generated_schema.json index fe881280d..94d3bbb66 100644 --- a/contrib/generated_schema.json +++ b/contrib/generated_schema.json @@ -604,6 +604,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -672,6 +676,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", diff --git a/contrib/openapi.json b/contrib/openapi.json index 1c0a9e17e..e298cf734 100644 --- a/contrib/openapi.json +++ b/contrib/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "NetBox REST API", - "version": "4.6.2", + "version": "4.6.3", "license": { "name": "Apache v2 License" } @@ -12,6 +12,32 @@ "get": { "operationId": "authentication_check_retrieve", "description": "Return the user making the request, if authenticated successfully.", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "authentication-check" ], @@ -43,6 +69,14 @@ "operationId": "circuits_circuit_group_assignments_list", "description": "Get a list of Circuit group assignment objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "circuit", @@ -167,6 +201,14 @@ "format": "uuid" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -530,6 +572,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -922,7 +972,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupAssignmentRequest" + "$ref": "#/components/schemas/BulkCircuitGroupAssignmentRequest" } } }, @@ -930,7 +980,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupAssignmentRequest" + "$ref": "#/components/schemas/BulkCircuitGroupAssignmentRequest" } } } @@ -973,7 +1023,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupAssignmentRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitGroupAssignmentRequest" } } }, @@ -981,7 +1031,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupAssignmentRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitGroupAssignmentRequest" } } } @@ -1059,6 +1109,22 @@ "operationId": "circuits_circuit_group_assignments_retrieve", "description": "Get a Circuit group assignment object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -1067,6 +1133,14 @@ }, "description": "A unique integer value identifying this Circuit group assignment.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -1235,6 +1309,14 @@ "operationId": "circuits_circuit_groups_list", "description": "Get a list of circuit group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -1485,6 +1567,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -1838,6 +1928,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -2378,7 +2476,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupRequest" + "$ref": "#/components/schemas/BulkCircuitGroupRequest" } } }, @@ -2386,7 +2484,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupRequest" + "$ref": "#/components/schemas/BulkCircuitGroupRequest" } } } @@ -2429,7 +2527,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitGroupRequest" } } }, @@ -2437,7 +2535,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitGroupRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitGroupRequest" } } } @@ -2515,6 +2613,22 @@ "operationId": "circuits_circuit_groups_retrieve", "description": "Get a circuit group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -2523,6 +2637,14 @@ }, "description": "A unique integer value identifying this circuit group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -2691,6 +2813,14 @@ "operationId": "circuits_circuit_terminations_list", "description": "Get a list of circuit termination objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -3244,6 +3374,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -3508,6 +3646,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -4653,7 +4799,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTerminationRequest" + "$ref": "#/components/schemas/BulkCircuitTerminationRequest" } } }, @@ -4661,7 +4807,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTerminationRequest" + "$ref": "#/components/schemas/BulkCircuitTerminationRequest" } } } @@ -4704,7 +4850,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitTerminationRequest" } } }, @@ -4712,7 +4858,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitTerminationRequest" } } } @@ -4790,6 +4936,22 @@ "operationId": "circuits_circuit_terminations_retrieve", "description": "Get a circuit termination object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -4798,6 +4960,14 @@ }, "description": "A unique integer value identifying this circuit termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -4966,6 +5136,22 @@ "operationId": "circuits_circuit_terminations_paths_retrieve", "description": "Return all CablePaths which traverse a given pass-through port.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -4974,6 +5160,14 @@ }, "description": "A unique integer value identifying this circuit termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -5006,6 +5200,14 @@ "operationId": "circuits_circuit_types_list", "description": "Get a list of circuit type objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -5407,6 +5609,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -5760,6 +5970,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -6198,7 +6416,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTypeRequest" + "$ref": "#/components/schemas/BulkCircuitTypeRequest" } } }, @@ -6206,7 +6424,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTypeRequest" + "$ref": "#/components/schemas/BulkCircuitTypeRequest" } } } @@ -6249,7 +6467,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTypeRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitTypeRequest" } } }, @@ -6257,7 +6475,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitTypeRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitTypeRequest" } } } @@ -6335,6 +6553,22 @@ "operationId": "circuits_circuit_types_retrieve", "description": "Get a circuit type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -6343,6 +6577,14 @@ }, "description": "A unique integer value identifying this circuit type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -6511,6 +6753,14 @@ "operationId": "circuits_circuits_list", "description": "Get a list of circuit objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cid", @@ -7319,6 +7569,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -7632,6 +7890,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -8657,7 +8923,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitRequest" + "$ref": "#/components/schemas/BulkCircuitRequest" } } }, @@ -8665,7 +8931,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitRequest" + "$ref": "#/components/schemas/BulkCircuitRequest" } } } @@ -8708,7 +8974,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitRequest" } } }, @@ -8716,7 +8982,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CircuitRequest" + "$ref": "#/components/schemas/PatchedBulkCircuitRequest" } } } @@ -8794,6 +9060,22 @@ "operationId": "circuits_circuits_retrieve", "description": "Get a circuit object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -8802,6 +9084,14 @@ }, "description": "A unique integer value identifying this circuit.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -9121,6 +9411,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -9447,6 +9745,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -9800,6 +10106,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -10139,7 +10453,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderAccountRequest" + "$ref": "#/components/schemas/BulkProviderAccountRequest" } } }, @@ -10147,7 +10461,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderAccountRequest" + "$ref": "#/components/schemas/BulkProviderAccountRequest" } } } @@ -10190,7 +10504,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderAccountRequest" + "$ref": "#/components/schemas/PatchedBulkProviderAccountRequest" } } }, @@ -10198,7 +10512,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderAccountRequest" + "$ref": "#/components/schemas/PatchedBulkProviderAccountRequest" } } } @@ -10276,6 +10590,22 @@ "operationId": "circuits_provider_accounts_retrieve", "description": "Get a provider account object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -10284,6 +10614,14 @@ }, "description": "A unique integer value identifying this provider account.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -10452,6 +10790,14 @@ "operationId": "circuits_provider_networks_list", "description": "Get a list of provider network objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -10702,6 +11048,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -11055,6 +11409,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -11545,7 +11907,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderNetworkRequest" + "$ref": "#/components/schemas/BulkProviderNetworkRequest" } } }, @@ -11553,7 +11915,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderNetworkRequest" + "$ref": "#/components/schemas/BulkProviderNetworkRequest" } } } @@ -11596,7 +11958,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderNetworkRequest" + "$ref": "#/components/schemas/PatchedBulkProviderNetworkRequest" } } }, @@ -11604,7 +11966,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderNetworkRequest" + "$ref": "#/components/schemas/PatchedBulkProviderNetworkRequest" } } } @@ -11682,6 +12044,22 @@ "operationId": "circuits_provider_networks_retrieve", "description": "Get a provider network object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -11690,6 +12068,14 @@ }, "description": "A unique integer value identifying this provider network.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -11916,6 +12302,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -12242,6 +12636,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -12595,6 +12997,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -13181,7 +13591,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderRequest" + "$ref": "#/components/schemas/BulkProviderRequest" } } }, @@ -13189,7 +13599,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderRequest" + "$ref": "#/components/schemas/BulkProviderRequest" } } } @@ -13232,7 +13642,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderRequest" + "$ref": "#/components/schemas/PatchedBulkProviderRequest" } } }, @@ -13240,7 +13650,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ProviderRequest" + "$ref": "#/components/schemas/PatchedBulkProviderRequest" } } } @@ -13318,6 +13728,22 @@ "operationId": "circuits_providers_retrieve", "description": "Get a provider object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -13326,6 +13752,14 @@ }, "description": "A unique integer value identifying this provider.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -13494,6 +13928,14 @@ "operationId": "circuits_virtual_circuit_terminations_list", "description": "Get a list of virtual circuit termination objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -13744,6 +14186,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -13972,6 +14422,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -14474,7 +14932,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTerminationRequest" + "$ref": "#/components/schemas/BulkVirtualCircuitTerminationRequest" } } }, @@ -14482,7 +14940,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTerminationRequest" + "$ref": "#/components/schemas/BulkVirtualCircuitTerminationRequest" } } } @@ -14525,7 +14983,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualCircuitTerminationRequest" } } }, @@ -14533,7 +14991,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualCircuitTerminationRequest" } } } @@ -14611,6 +15069,22 @@ "operationId": "circuits_virtual_circuit_terminations_retrieve", "description": "Get a virtual circuit termination object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -14619,6 +15093,14 @@ }, "description": "A unique integer value identifying this virtual circuit termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -14787,6 +15269,22 @@ "operationId": "circuits_virtual_circuit_terminations_paths_retrieve", "description": "Return all CablePaths which traverse a given pass-through port.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -14795,6 +15293,14 @@ }, "description": "A unique integer value identifying this virtual circuit termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -14827,6 +15333,14 @@ "operationId": "circuits_virtual_circuit_types_list", "description": "Get a list of virtual circuit type objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -15228,6 +15742,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -15581,6 +16103,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -16019,7 +16549,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTypeRequest" + "$ref": "#/components/schemas/BulkVirtualCircuitTypeRequest" } } }, @@ -16027,7 +16557,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTypeRequest" + "$ref": "#/components/schemas/BulkVirtualCircuitTypeRequest" } } } @@ -16070,7 +16600,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTypeRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualCircuitTypeRequest" } } }, @@ -16078,7 +16608,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitTypeRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualCircuitTypeRequest" } } } @@ -16156,6 +16686,22 @@ "operationId": "circuits_virtual_circuit_types_retrieve", "description": "Get a virtual circuit type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -16164,6 +16710,14 @@ }, "description": "A unique integer value identifying this virtual circuit type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -16332,6 +16886,14 @@ "operationId": "circuits_virtual_circuits_list", "description": "Get a list of virtual circuit objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cid", @@ -16733,6 +17295,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -16935,6 +17505,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -17671,7 +18249,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitRequest" + "$ref": "#/components/schemas/BulkVirtualCircuitRequest" } } }, @@ -17679,7 +18257,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitRequest" + "$ref": "#/components/schemas/BulkVirtualCircuitRequest" } } } @@ -17722,7 +18300,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualCircuitRequest" } } }, @@ -17730,7 +18308,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualCircuitRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualCircuitRequest" } } } @@ -17808,6 +18386,22 @@ "operationId": "circuits_virtual_circuits_retrieve", "description": "Get a virtual circuit object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -17816,6 +18410,14 @@ }, "description": "A unique integer value identifying this virtual circuit.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -17983,6 +18585,32 @@ "get": { "operationId": "core_background_queues_retrieve", "description": "Retrieve a list of RQ Queues.\nNote: Queue names are not URL safe, so not returning a detail view.", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "core" ], @@ -18014,6 +18642,22 @@ "operationId": "core_background_queues_retrieve_by_name", "description": "Retrieve a list of RQ Queues.\nNote: Queue names are not URL safe, so not returning a detail view.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "name", @@ -18021,6 +18665,14 @@ "type": "string" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -18053,6 +18705,32 @@ "get": { "operationId": "core_background_tasks_retrieve", "description": "Retrieve a list of RQ Tasks.", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "core" ], @@ -18084,6 +18762,22 @@ "operationId": "core_background_tasks_retrieve_by_id", "description": "Retrieve a list of RQ Tasks.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -18091,6 +18785,14 @@ "type": "string" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -18339,6 +19041,32 @@ "get": { "operationId": "core_background_workers_retrieve", "description": "Retrieve a list of RQ Workers.", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "core" ], @@ -18370,6 +19098,22 @@ "operationId": "core_background_workers_retrieve_by_name", "description": "Retrieve a list of RQ Workers.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "name", @@ -18377,6 +19121,14 @@ "type": "string" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -18410,6 +19162,14 @@ "operationId": "core_data_files_list", "description": "Get a list of data file objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -18509,6 +19269,14 @@ "format": "uuid" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "hash", @@ -18862,6 +19630,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -19214,6 +19990,22 @@ "operationId": "core_data_files_retrieve", "description": "Get a data file object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -19222,6 +20014,14 @@ }, "description": "A unique integer value identifying this data file.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -19254,6 +20054,14 @@ "operationId": "core_data_sources_list", "description": "Get a list of data source objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -19511,6 +20319,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -19949,6 +20765,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -20857,7 +21681,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DataSourceRequest" + "$ref": "#/components/schemas/BulkDataSourceRequest" } } }, @@ -20865,7 +21689,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DataSourceRequest" + "$ref": "#/components/schemas/BulkDataSourceRequest" } } } @@ -20908,7 +21732,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DataSourceRequest" + "$ref": "#/components/schemas/PatchedBulkDataSourceRequest" } } }, @@ -20916,7 +21740,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DataSourceRequest" + "$ref": "#/components/schemas/PatchedBulkDataSourceRequest" } } } @@ -20994,6 +21818,22 @@ "operationId": "core_data_sources_retrieve", "description": "Get a data source object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -21002,6 +21842,14 @@ }, "description": "A unique integer value identifying this data source.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -21225,6 +22073,14 @@ "operationId": "core_jobs_list", "description": "Retrieve a list of job results", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "completed", @@ -21273,6 +22129,14 @@ "format": "date-time" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -21918,6 +22782,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -22331,6 +23203,22 @@ "operationId": "core_jobs_retrieve", "description": "Retrieve a list of job results", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -22339,6 +23227,14 @@ }, "description": "A unique integer value identifying this job.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -22528,6 +23424,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "changed_object_id", @@ -22661,6 +23565,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -22915,6 +23827,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -23310,6 +24230,22 @@ "operationId": "core_object_changes_retrieve", "description": "Retrieve a list of recent changes.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -23318,6 +24254,14 @@ }, "description": "A unique integer value identifying this object change.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -23501,6 +24445,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "features", @@ -23508,6 +24460,14 @@ "type": "string" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -23762,6 +24722,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -23826,6 +24794,22 @@ "operationId": "core_object_types_retrieve", "description": "Read-only list of ObjectTypes.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -23834,6 +24818,14 @@ }, "description": "A unique integer value identifying this object type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -23866,6 +24858,14 @@ "operationId": "dcim_cable_bundles_list", "description": "Get a list of cable bundle objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -24116,6 +25116,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -24469,6 +25477,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -24756,7 +25772,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableBundleRequest" + "$ref": "#/components/schemas/BulkCableBundleRequest" } } }, @@ -24764,7 +25780,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableBundleRequest" + "$ref": "#/components/schemas/BulkCableBundleRequest" } } } @@ -24807,7 +25823,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableBundleRequest" + "$ref": "#/components/schemas/PatchedBulkCableBundleRequest" } } }, @@ -24815,7 +25831,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableBundleRequest" + "$ref": "#/components/schemas/PatchedBulkCableBundleRequest" } } } @@ -24893,6 +25909,22 @@ "operationId": "dcim_cable_bundles_retrieve", "description": "Get a cable bundle object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -24901,6 +25933,14 @@ }, "description": "A unique integer value identifying this cable bundle.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -25069,6 +26109,14 @@ "operationId": "dcim_cable_terminations_list", "description": "Get a list of cable termination objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable", @@ -25404,6 +26452,14 @@ "format": "uuid" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "frontport_id", @@ -25632,6 +26688,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -25850,6 +26914,22 @@ "operationId": "dcim_cable_terminations_retrieve", "description": "Get a cable termination object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -25858,6 +26938,14 @@ }, "description": "A unique integer value identifying this cable termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -25890,6 +26978,14 @@ "operationId": "dcim_cables_list", "description": "Get a list of cable objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "bundle", @@ -26409,6 +27505,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "frontport_id", @@ -27063,6 +28167,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -28136,7 +29248,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableRequest" + "$ref": "#/components/schemas/BulkCableRequest" } } }, @@ -28144,7 +29256,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableRequest" + "$ref": "#/components/schemas/BulkCableRequest" } } } @@ -28187,7 +29299,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableRequest" + "$ref": "#/components/schemas/PatchedBulkCableRequest" } } }, @@ -28195,7 +29307,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CableRequest" + "$ref": "#/components/schemas/PatchedBulkCableRequest" } } } @@ -28273,6 +29385,22 @@ "operationId": "dcim_cables_retrieve", "description": "Get a cable object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -28281,6 +29409,14 @@ }, "description": "A unique integer value identifying this cable.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -28448,6 +29584,30 @@ "operationId": "dcim_connected_device_list", "description": "This endpoint allows a user to determine what device (if any) is connected to a given peer device and peer\ninterface. This is useful in a situation where a device boots with no configuration, but can detect its neighbors\nvia a protocol such as LLDP. Two query parameters must be included in the request:\n\n* `peer_device`: The name of the peer device\n* `peer_interface`: The name of the peer interface", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "in": "query", "name": "peer_device", @@ -28500,6 +29660,14 @@ "operationId": "dcim_console_port_templates_list", "description": "Get a list of console port template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -28778,6 +29946,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -29310,6 +30486,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -29602,7 +30786,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortTemplateRequest" + "$ref": "#/components/schemas/BulkConsolePortTemplateRequest" } } }, @@ -29610,7 +30794,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortTemplateRequest" + "$ref": "#/components/schemas/BulkConsolePortTemplateRequest" } } } @@ -29653,7 +30837,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkConsolePortTemplateRequest" } } }, @@ -29661,7 +30845,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkConsolePortTemplateRequest" } } } @@ -29739,6 +30923,22 @@ "operationId": "dcim_console_port_templates_retrieve", "description": "Get a console port template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -29747,6 +30947,14 @@ }, "description": "A unique integer value identifying this console port template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -29915,6 +31123,14 @@ "operationId": "dcim_console_ports_list", "description": "Get a list of console port objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -30770,6 +31986,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -31368,6 +32592,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -32318,7 +33550,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortRequest" + "$ref": "#/components/schemas/BulkConsolePortRequest" } } }, @@ -32326,7 +33558,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortRequest" + "$ref": "#/components/schemas/BulkConsolePortRequest" } } } @@ -32369,7 +33601,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortRequest" + "$ref": "#/components/schemas/PatchedBulkConsolePortRequest" } } }, @@ -32377,7 +33609,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsolePortRequest" + "$ref": "#/components/schemas/PatchedBulkConsolePortRequest" } } } @@ -32455,6 +33687,22 @@ "operationId": "dcim_console_ports_retrieve", "description": "Get a console port object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -32463,6 +33711,14 @@ }, "description": "A unique integer value identifying this console port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -32631,6 +33887,22 @@ "operationId": "dcim_console_ports_trace_retrieve", "description": "Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -32639,6 +33911,14 @@ }, "description": "A unique integer value identifying this console port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -32671,6 +33951,14 @@ "operationId": "dcim_console_server_port_templates_list", "description": "Get a list of console server port template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -32949,6 +34237,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -33481,6 +34777,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -33773,7 +35077,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortTemplateRequest" + "$ref": "#/components/schemas/BulkConsoleServerPortTemplateRequest" } } }, @@ -33781,7 +35085,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortTemplateRequest" + "$ref": "#/components/schemas/BulkConsoleServerPortTemplateRequest" } } } @@ -33824,7 +35128,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkConsoleServerPortTemplateRequest" } } }, @@ -33832,7 +35136,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkConsoleServerPortTemplateRequest" } } } @@ -33910,6 +35214,22 @@ "operationId": "dcim_console_server_port_templates_retrieve", "description": "Get a console server port template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -33918,6 +35238,14 @@ }, "description": "A unique integer value identifying this console server port template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -34086,6 +35414,14 @@ "operationId": "dcim_console_server_ports_list", "description": "Get a list of console server port objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -34941,6 +36277,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -35539,6 +36883,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -36489,7 +37841,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortRequest" + "$ref": "#/components/schemas/BulkConsoleServerPortRequest" } } }, @@ -36497,7 +37849,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortRequest" + "$ref": "#/components/schemas/BulkConsoleServerPortRequest" } } } @@ -36540,7 +37892,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortRequest" + "$ref": "#/components/schemas/PatchedBulkConsoleServerPortRequest" } } }, @@ -36548,7 +37900,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsoleServerPortRequest" + "$ref": "#/components/schemas/PatchedBulkConsoleServerPortRequest" } } } @@ -36626,6 +37978,22 @@ "operationId": "dcim_console_server_ports_retrieve", "description": "Get a console server port object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -36634,6 +38002,14 @@ }, "description": "A unique integer value identifying this console server port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -36802,6 +38178,22 @@ "operationId": "dcim_console_server_ports_trace_retrieve", "description": "Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -36810,6 +38202,14 @@ }, "description": "A unique integer value identifying this console server port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -36842,6 +38242,14 @@ "operationId": "dcim_device_bay_templates_list", "description": "Get a list of device bay template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -37125,6 +38533,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -37629,6 +39045,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -37762,7 +39186,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayTemplateRequest" + "$ref": "#/components/schemas/BulkDeviceBayTemplateRequest" } } }, @@ -37770,7 +39194,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayTemplateRequest" + "$ref": "#/components/schemas/BulkDeviceBayTemplateRequest" } } } @@ -37813,7 +39237,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceBayTemplateRequest" } } }, @@ -37821,7 +39245,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceBayTemplateRequest" } } } @@ -37899,6 +39323,22 @@ "operationId": "dcim_device_bay_templates_retrieve", "description": "Get a device bay template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -37907,6 +39347,14 @@ }, "description": "A unique integer value identifying this device bay template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -38075,6 +39523,14 @@ "operationId": "dcim_device_bays_list", "description": "Get a list of device bay objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -38653,6 +40109,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -39265,6 +40729,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -39856,7 +41328,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayRequest" + "$ref": "#/components/schemas/BulkDeviceBayRequest" } } }, @@ -39864,7 +41336,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayRequest" + "$ref": "#/components/schemas/BulkDeviceBayRequest" } } } @@ -39907,7 +41379,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceBayRequest" } } }, @@ -39915,7 +41387,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceBayRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceBayRequest" } } } @@ -39993,6 +41465,22 @@ "operationId": "dcim_device_bays_retrieve", "description": "Get a device bay object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -40001,6 +41489,14 @@ }, "description": "A unique integer value identifying this device bay.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -40217,6 +41713,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -40646,6 +42150,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -40999,6 +42511,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -41498,7 +43018,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceRoleRequest" + "$ref": "#/components/schemas/BulkDeviceRoleRequest" } } }, @@ -41506,7 +43026,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceRoleRequest" + "$ref": "#/components/schemas/BulkDeviceRoleRequest" } } } @@ -41549,7 +43069,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceRoleRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceRoleRequest" } } }, @@ -41557,7 +43077,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceRoleRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceRoleRequest" } } } @@ -41635,6 +43155,22 @@ "operationId": "dcim_device_roles_retrieve", "description": "Get a device role object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -41643,6 +43179,14 @@ }, "description": "A unique integer value identifying this device role.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -41984,6 +43528,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "console_port_template_count", @@ -42653,6 +44205,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "front_port_template_count", @@ -43445,6 +45005,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -44803,7 +46371,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceTypeRequest" + "$ref": "#/components/schemas/BulkDeviceTypeRequest" } } }, @@ -44811,7 +46379,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceTypeRequest" + "$ref": "#/components/schemas/BulkDeviceTypeRequest" } } } @@ -44854,7 +46422,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceTypeRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceTypeRequest" } } }, @@ -44862,7 +46430,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceTypeRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceTypeRequest" } } } @@ -44940,6 +46508,22 @@ "operationId": "dcim_device_types_retrieve", "description": "Get a device type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -44948,6 +46532,14 @@ }, "description": "A unique integer value identifying this device type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -45440,6 +47032,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cluster_group", @@ -46364,6 +47964,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "front_port_count", @@ -47553,6 +49161,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "in": "query", "name": "oob_ip_id", @@ -49276,7 +50892,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceWithConfigContextRequest" + "$ref": "#/components/schemas/BulkDeviceWithConfigContextRequest" } } }, @@ -49284,7 +50900,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceWithConfigContextRequest" + "$ref": "#/components/schemas/BulkDeviceWithConfigContextRequest" } } } @@ -49327,7 +50943,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceWithConfigContextRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceWithConfigContextRequest" } } }, @@ -49335,7 +50951,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/DeviceWithConfigContextRequest" + "$ref": "#/components/schemas/PatchedBulkDeviceWithConfigContextRequest" } } } @@ -49413,6 +51029,22 @@ "operationId": "dcim_devices_retrieve", "description": "Get a device object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -49421,6 +51053,14 @@ }, "description": "A unique integer value identifying this device.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -49693,6 +51333,14 @@ "operationId": "dcim_front_port_templates_list", "description": "Get a list of front port template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -50122,6 +51770,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -50654,6 +52310,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -50798,7 +52462,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50818,7 +52482,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50831,7 +52495,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50844,7 +52508,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50857,7 +52521,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50870,7 +52534,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50883,7 +52547,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50896,7 +52560,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50909,7 +52573,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50922,7 +52586,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50935,7 +52599,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -50948,7 +52612,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -51061,7 +52725,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortTemplateRequest" + "$ref": "#/components/schemas/BulkFrontPortTemplateRequest" } } }, @@ -51069,7 +52733,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortTemplateRequest" + "$ref": "#/components/schemas/BulkFrontPortTemplateRequest" } } } @@ -51112,7 +52776,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkFrontPortTemplateRequest" } } }, @@ -51120,7 +52784,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkFrontPortTemplateRequest" } } } @@ -51198,6 +52862,22 @@ "operationId": "dcim_front_port_templates_retrieve", "description": "Get a front port template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -51206,6 +52886,14 @@ }, "description": "A unique integer value identifying this front port template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -51374,6 +53062,14 @@ "operationId": "dcim_front_ports_list", "description": "Get a list of front port objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -52373,6 +54069,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -52971,6 +54675,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -53521,7 +55233,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53541,7 +55253,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53554,7 +55266,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53567,7 +55279,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53580,7 +55292,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53593,7 +55305,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53606,7 +55318,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53619,7 +55331,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53632,7 +55344,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53645,7 +55357,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53658,7 +55370,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53671,7 +55383,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -53836,7 +55548,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortRequest" + "$ref": "#/components/schemas/BulkFrontPortRequest" } } }, @@ -53844,7 +55556,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortRequest" + "$ref": "#/components/schemas/BulkFrontPortRequest" } } } @@ -53887,7 +55599,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortRequest" + "$ref": "#/components/schemas/PatchedBulkFrontPortRequest" } } }, @@ -53895,7 +55607,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FrontPortRequest" + "$ref": "#/components/schemas/PatchedBulkFrontPortRequest" } } } @@ -53973,6 +55685,22 @@ "operationId": "dcim_front_ports_retrieve", "description": "Get a front port object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -53981,6 +55709,14 @@ }, "description": "A unique integer value identifying this front port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -54149,6 +55885,22 @@ "operationId": "dcim_front_ports_paths_retrieve", "description": "Return all CablePaths which traverse a given pass-through port.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -54157,6 +55909,14 @@ }, "description": "A unique integer value identifying this front port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -54213,6 +55973,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -54498,6 +56266,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -55037,6 +56813,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -55870,7 +57654,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceTemplateRequest" + "$ref": "#/components/schemas/BulkInterfaceTemplateRequest" } } }, @@ -55878,7 +57662,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceTemplateRequest" + "$ref": "#/components/schemas/BulkInterfaceTemplateRequest" } } } @@ -55921,7 +57705,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkInterfaceTemplateRequest" } } }, @@ -55929,7 +57713,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkInterfaceTemplateRequest" } } } @@ -56007,6 +57791,22 @@ "operationId": "dcim_interface_templates_retrieve", "description": "Get a interface template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -56015,6 +57815,14 @@ }, "description": "A unique integer value identifying this interface template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -56209,6 +58017,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -57246,6 +59062,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -58362,6 +60186,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -60772,7 +62604,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceRequest" + "$ref": "#/components/schemas/BulkInterfaceRequest" } } }, @@ -60780,7 +62612,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceRequest" + "$ref": "#/components/schemas/BulkInterfaceRequest" } } } @@ -60823,7 +62655,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceRequest" + "$ref": "#/components/schemas/PatchedBulkInterfaceRequest" } } }, @@ -60831,7 +62663,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InterfaceRequest" + "$ref": "#/components/schemas/PatchedBulkInterfaceRequest" } } } @@ -60909,6 +62741,22 @@ "operationId": "dcim_interfaces_retrieve", "description": "Get a interface object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -60917,6 +62765,14 @@ }, "description": "A unique integer value identifying this interface.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -61085,6 +62941,22 @@ "operationId": "dcim_interfaces_trace_retrieve", "description": "Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -61093,6 +62965,14 @@ }, "description": "A unique integer value identifying this interface.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -61125,6 +63005,14 @@ "operationId": "dcim_inventory_item_roles_list", "description": "Get a list of inventory item role objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -61526,6 +63414,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -61879,6 +63775,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -62317,7 +64221,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRoleRequest" + "$ref": "#/components/schemas/BulkInventoryItemRoleRequest" } } }, @@ -62325,7 +64229,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRoleRequest" + "$ref": "#/components/schemas/BulkInventoryItemRoleRequest" } } } @@ -62368,7 +64272,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRoleRequest" + "$ref": "#/components/schemas/PatchedBulkInventoryItemRoleRequest" } } }, @@ -62376,7 +64280,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRoleRequest" + "$ref": "#/components/schemas/PatchedBulkInventoryItemRoleRequest" } } } @@ -62454,6 +64358,22 @@ "operationId": "dcim_inventory_item_roles_retrieve", "description": "Get a inventory item role object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -62462,6 +64382,14 @@ }, "description": "A unique integer value identifying this inventory item role.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -62630,6 +64558,14 @@ "operationId": "dcim_inventory_item_templates_list", "description": "Get a list of inventory item template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "component_id", @@ -63021,6 +64957,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -63579,6 +65523,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -63945,7 +65897,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemTemplateRequest" + "$ref": "#/components/schemas/BulkInventoryItemTemplateRequest" } } }, @@ -63953,7 +65905,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemTemplateRequest" + "$ref": "#/components/schemas/BulkInventoryItemTemplateRequest" } } } @@ -63996,7 +65948,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkInventoryItemTemplateRequest" } } }, @@ -64004,7 +65956,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkInventoryItemTemplateRequest" } } } @@ -64082,6 +66034,22 @@ "operationId": "dcim_inventory_item_templates_retrieve", "description": "Get a inventory item template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -64090,6 +66058,14 @@ }, "description": "A unique integer value identifying this inventory item template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -64409,6 +66385,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "component_id", @@ -65102,6 +67086,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -65712,6 +67704,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -66850,7 +68850,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRequest" + "$ref": "#/components/schemas/BulkInventoryItemRequest" } } }, @@ -66858,7 +68858,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRequest" + "$ref": "#/components/schemas/BulkInventoryItemRequest" } } } @@ -66901,7 +68901,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRequest" + "$ref": "#/components/schemas/PatchedBulkInventoryItemRequest" } } }, @@ -66909,7 +68909,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryItemRequest" + "$ref": "#/components/schemas/PatchedBulkInventoryItemRequest" } } } @@ -66987,6 +68987,22 @@ "operationId": "dcim_inventory_items_retrieve", "description": "Get a inventory item object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -66995,6 +69011,14 @@ }, "description": "A unique integer value identifying this inventory item.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -67211,6 +69235,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -67688,6 +69720,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -68041,6 +70081,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -68946,7 +70994,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/LocationRequest" + "$ref": "#/components/schemas/BulkLocationRequest" } } }, @@ -68954,7 +71002,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/LocationRequest" + "$ref": "#/components/schemas/BulkLocationRequest" } } } @@ -68997,7 +71045,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/LocationRequest" + "$ref": "#/components/schemas/PatchedBulkLocationRequest" } } }, @@ -69005,7 +71053,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/LocationRequest" + "$ref": "#/components/schemas/PatchedBulkLocationRequest" } } } @@ -69083,6 +71131,22 @@ "operationId": "dcim_locations_retrieve", "description": "Get a location object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -69091,6 +71155,14 @@ }, "description": "A unique integer value identifying this location.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -69376,6 +71448,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -69651,6 +71731,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -70049,6 +72137,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -70421,7 +72517,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MACAddressRequest" + "$ref": "#/components/schemas/BulkMACAddressRequest" } } }, @@ -70429,7 +72525,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MACAddressRequest" + "$ref": "#/components/schemas/BulkMACAddressRequest" } } } @@ -70472,7 +72568,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MACAddressRequest" + "$ref": "#/components/schemas/PatchedBulkMACAddressRequest" } } }, @@ -70480,7 +72576,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MACAddressRequest" + "$ref": "#/components/schemas/PatchedBulkMACAddressRequest" } } } @@ -70558,6 +72654,22 @@ "operationId": "dcim_mac_addresses_retrieve", "description": "Get a MAC address object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -70566,6 +72678,14 @@ }, "description": "A unique integer value identifying this MAC address.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -70734,6 +72854,14 @@ "operationId": "dcim_manufacturers_list", "description": "Get a list of manufacturer objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -71060,6 +73188,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -71413,6 +73549,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -71851,7 +73995,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ManufacturerRequest" + "$ref": "#/components/schemas/BulkManufacturerRequest" } } }, @@ -71859,7 +74003,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ManufacturerRequest" + "$ref": "#/components/schemas/BulkManufacturerRequest" } } } @@ -71902,7 +74046,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ManufacturerRequest" + "$ref": "#/components/schemas/PatchedBulkManufacturerRequest" } } }, @@ -71910,7 +74054,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ManufacturerRequest" + "$ref": "#/components/schemas/PatchedBulkManufacturerRequest" } } } @@ -71988,6 +74132,22 @@ "operationId": "dcim_manufacturers_retrieve", "description": "Get a manufacturer object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -71996,6 +74156,14 @@ }, "description": "A unique integer value identifying this manufacturer.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -72164,6 +74332,14 @@ "operationId": "dcim_module_bay_templates_list", "description": "Get a list of module bay template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -72449,6 +74625,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -72981,6 +75165,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -73265,7 +75457,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayTemplateRequest" + "$ref": "#/components/schemas/BulkModuleBayTemplateRequest" } } }, @@ -73273,7 +75465,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayTemplateRequest" + "$ref": "#/components/schemas/BulkModuleBayTemplateRequest" } } } @@ -73316,7 +75508,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkModuleBayTemplateRequest" } } }, @@ -73324,7 +75516,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkModuleBayTemplateRequest" } } } @@ -73402,6 +75594,22 @@ "operationId": "dcim_module_bay_templates_retrieve", "description": "Get a module bay template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -73410,6 +75618,14 @@ }, "description": "A unique integer value identifying this module bay template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -73578,6 +75794,14 @@ "operationId": "dcim_module_bays_list", "description": "Get a list of module bay objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -74156,6 +76380,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -74766,6 +76998,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -75536,7 +77776,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayRequest" + "$ref": "#/components/schemas/BulkModuleBayRequest" } } }, @@ -75544,7 +77784,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayRequest" + "$ref": "#/components/schemas/BulkModuleBayRequest" } } } @@ -75587,7 +77827,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayRequest" + "$ref": "#/components/schemas/PatchedBulkModuleBayRequest" } } }, @@ -75595,7 +77835,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleBayRequest" + "$ref": "#/components/schemas/PatchedBulkModuleBayRequest" } } } @@ -75673,6 +77913,22 @@ "operationId": "dcim_module_bays_retrieve", "description": "Get a module bay object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -75681,6 +77937,14 @@ }, "description": "A unique integer value identifying this module bay.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -75849,6 +78113,14 @@ "operationId": "dcim_module_type_profiles_list", "description": "Get a list of module type profile objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -76099,6 +78371,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -76452,6 +78732,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -76739,7 +79027,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeProfileRequest" + "$ref": "#/components/schemas/BulkModuleTypeProfileRequest" } } }, @@ -76747,7 +79035,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeProfileRequest" + "$ref": "#/components/schemas/BulkModuleTypeProfileRequest" } } } @@ -76790,7 +79078,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeProfileRequest" + "$ref": "#/components/schemas/PatchedBulkModuleTypeProfileRequest" } } }, @@ -76798,7 +79086,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeProfileRequest" + "$ref": "#/components/schemas/PatchedBulkModuleTypeProfileRequest" } } } @@ -76876,6 +79164,22 @@ "operationId": "dcim_module_type_profiles_retrieve", "description": "Get a module type profile object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -76884,6 +79188,14 @@ }, "description": "A unique integer value identifying this module type profile.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -77217,6 +79529,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "console_port_template_count", @@ -77653,6 +79973,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "front_port_template_count", @@ -78414,6 +80742,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -79431,7 +81767,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeRequest" + "$ref": "#/components/schemas/BulkModuleTypeRequest" } } }, @@ -79439,7 +81775,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeRequest" + "$ref": "#/components/schemas/BulkModuleTypeRequest" } } } @@ -79482,7 +81818,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeRequest" + "$ref": "#/components/schemas/PatchedBulkModuleTypeRequest" } } }, @@ -79490,7 +81826,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleTypeRequest" + "$ref": "#/components/schemas/PatchedBulkModuleTypeRequest" } } } @@ -79568,6 +81904,22 @@ "operationId": "dcim_module_types_retrieve", "description": "Get a module type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -79576,6 +81928,14 @@ }, "description": "A unique integer value identifying this module type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -79895,6 +82255,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -80199,6 +82567,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -80581,6 +82957,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -81434,7 +83818,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleRequest" + "$ref": "#/components/schemas/BulkModuleRequest" } } }, @@ -81442,7 +83826,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleRequest" + "$ref": "#/components/schemas/BulkModuleRequest" } } } @@ -81485,7 +83869,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleRequest" + "$ref": "#/components/schemas/PatchedBulkModuleRequest" } } }, @@ -81493,7 +83877,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ModuleRequest" + "$ref": "#/components/schemas/PatchedBulkModuleRequest" } } } @@ -81571,6 +83955,22 @@ "operationId": "dcim_modules_retrieve", "description": "Get a module object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -81579,6 +83979,14 @@ }, "description": "A unique integer value identifying this module.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -81802,6 +84210,14 @@ "type": "string" } }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "config_template_id", @@ -82080,6 +84496,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -82485,6 +84909,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -82977,7 +85409,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PlatformRequest" + "$ref": "#/components/schemas/BulkPlatformRequest" } } }, @@ -82985,7 +85417,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PlatformRequest" + "$ref": "#/components/schemas/BulkPlatformRequest" } } } @@ -83028,7 +85460,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PlatformRequest" + "$ref": "#/components/schemas/PatchedBulkPlatformRequest" } } }, @@ -83036,7 +85468,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PlatformRequest" + "$ref": "#/components/schemas/PatchedBulkPlatformRequest" } } } @@ -83114,6 +85546,22 @@ "operationId": "dcim_platforms_retrieve", "description": "Get a platform object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -83122,6 +85570,14 @@ }, "description": "A unique integer value identifying this platform.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -83460,6 +85916,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -83994,6 +86458,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -84446,6 +86918,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -85748,7 +88228,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerFeedRequest" + "$ref": "#/components/schemas/BulkPowerFeedRequest" } } }, @@ -85756,7 +88236,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerFeedRequest" + "$ref": "#/components/schemas/BulkPowerFeedRequest" } } } @@ -85799,7 +88279,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerFeedRequest" + "$ref": "#/components/schemas/PatchedBulkPowerFeedRequest" } } }, @@ -85807,7 +88287,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerFeedRequest" + "$ref": "#/components/schemas/PatchedBulkPowerFeedRequest" } } } @@ -85885,6 +88365,22 @@ "operationId": "dcim_power_feeds_retrieve", "description": "Get a power feed object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -85893,6 +88389,14 @@ }, "description": "A unique integer value identifying this power feed.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -86061,6 +88565,22 @@ "operationId": "dcim_power_feeds_trace_retrieve", "description": "Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -86069,6 +88589,14 @@ }, "description": "A unique integer value identifying this power feed.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -86101,6 +88629,14 @@ "operationId": "dcim_power_outlet_templates_list", "description": "Get a list of power outlet template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -86717,6 +89253,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -87249,6 +89793,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -87587,7 +90139,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletTemplateRequest" + "$ref": "#/components/schemas/BulkPowerOutletTemplateRequest" } } }, @@ -87595,7 +90147,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletTemplateRequest" + "$ref": "#/components/schemas/BulkPowerOutletTemplateRequest" } } } @@ -87638,7 +90190,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkPowerOutletTemplateRequest" } } }, @@ -87646,7 +90198,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkPowerOutletTemplateRequest" } } } @@ -87724,6 +90276,22 @@ "operationId": "dcim_power_outlet_templates_retrieve", "description": "Get a power outlet template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -87732,6 +90300,14 @@ }, "description": "A unique integer value identifying this power outlet template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -87900,6 +90476,14 @@ "operationId": "dcim_power_outlets_list", "description": "Get a list of power outlet objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -89093,6 +91677,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -89691,6 +92283,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -90660,7 +93260,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletRequest" + "$ref": "#/components/schemas/BulkPowerOutletRequest" } } }, @@ -90668,7 +93268,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletRequest" + "$ref": "#/components/schemas/BulkPowerOutletRequest" } } } @@ -90711,7 +93311,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletRequest" + "$ref": "#/components/schemas/PatchedBulkPowerOutletRequest" } } }, @@ -90719,7 +93319,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerOutletRequest" + "$ref": "#/components/schemas/PatchedBulkPowerOutletRequest" } } } @@ -90797,6 +93397,22 @@ "operationId": "dcim_power_outlets_retrieve", "description": "Get a power outlet object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -90805,6 +93421,14 @@ }, "description": "A unique integer value identifying this power outlet.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -90973,6 +93597,22 @@ "operationId": "dcim_power_outlets_trace_retrieve", "description": "Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -90981,6 +93621,14 @@ }, "description": "A unique integer value identifying this power outlet.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -91013,6 +93661,14 @@ "operationId": "dcim_power_panels_list", "description": "Get a list of power panel objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -91339,6 +93995,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -91716,6 +94380,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -92151,7 +94823,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPanelRequest" + "$ref": "#/components/schemas/BulkPowerPanelRequest" } } }, @@ -92159,7 +94831,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPanelRequest" + "$ref": "#/components/schemas/BulkPowerPanelRequest" } } } @@ -92202,7 +94874,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPanelRequest" + "$ref": "#/components/schemas/PatchedBulkPowerPanelRequest" } } }, @@ -92210,7 +94882,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPanelRequest" + "$ref": "#/components/schemas/PatchedBulkPowerPanelRequest" } } } @@ -92288,6 +94960,22 @@ "operationId": "dcim_power_panels_retrieve", "description": "Get a power panel object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -92296,6 +94984,14 @@ }, "description": "A unique integer value identifying this power panel.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -92549,6 +95245,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -92827,6 +95531,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -93444,6 +96156,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -93754,7 +96474,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortTemplateRequest" + "$ref": "#/components/schemas/BulkPowerPortTemplateRequest" } } }, @@ -93762,7 +96482,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortTemplateRequest" + "$ref": "#/components/schemas/BulkPowerPortTemplateRequest" } } } @@ -93805,7 +96525,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkPowerPortTemplateRequest" } } }, @@ -93813,7 +96533,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkPowerPortTemplateRequest" } } } @@ -93891,6 +96611,22 @@ "operationId": "dcim_power_port_templates_retrieve", "description": "Get a power port template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -93899,6 +96635,14 @@ }, "description": "A unique integer value identifying this power port template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -94152,6 +96896,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -95007,6 +97759,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -95690,6 +98450,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -96468,7 +99236,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortRequest" + "$ref": "#/components/schemas/BulkPowerPortRequest" } } }, @@ -96476,7 +99244,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortRequest" + "$ref": "#/components/schemas/BulkPowerPortRequest" } } } @@ -96519,7 +99287,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortRequest" + "$ref": "#/components/schemas/PatchedBulkPowerPortRequest" } } }, @@ -96527,7 +99295,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PowerPortRequest" + "$ref": "#/components/schemas/PatchedBulkPowerPortRequest" } } } @@ -96605,6 +99373,22 @@ "operationId": "dcim_power_ports_retrieve", "description": "Get a power port object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -96613,6 +99397,14 @@ }, "description": "A unique integer value identifying this power port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -96781,6 +99573,22 @@ "operationId": "dcim_power_ports_trace_retrieve", "description": "Trace a complete cable path and return each segment as a three-tuple of (termination, cable, termination).", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -96789,6 +99597,14 @@ }, "description": "A unique integer value identifying this power port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -96821,6 +99637,14 @@ "operationId": "dcim_rack_groups_list", "description": "Get a list of rack group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -97071,6 +99895,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -97424,6 +100256,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -97862,7 +100702,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackGroupRequest" + "$ref": "#/components/schemas/BulkRackGroupRequest" } } }, @@ -97870,7 +100710,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackGroupRequest" + "$ref": "#/components/schemas/BulkRackGroupRequest" } } } @@ -97913,7 +100753,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackGroupRequest" + "$ref": "#/components/schemas/PatchedBulkRackGroupRequest" } } }, @@ -97921,7 +100761,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackGroupRequest" + "$ref": "#/components/schemas/PatchedBulkRackGroupRequest" } } } @@ -97999,6 +100839,22 @@ "operationId": "dcim_rack_groups_retrieve", "description": "Get a rack group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -98007,6 +100863,14 @@ }, "description": "A unique integer value identifying this rack group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -98175,6 +101039,14 @@ "operationId": "dcim_rack_reservations_list", "description": "Get a list of rack reservation objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -98425,6 +101297,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -98727,6 +101607,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -99570,7 +102458,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackReservationRequest" + "$ref": "#/components/schemas/BulkRackReservationRequest" } } }, @@ -99578,7 +102466,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackReservationRequest" + "$ref": "#/components/schemas/BulkRackReservationRequest" } } } @@ -99621,7 +102509,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackReservationRequest" + "$ref": "#/components/schemas/PatchedBulkRackReservationRequest" } } }, @@ -99629,7 +102517,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackReservationRequest" + "$ref": "#/components/schemas/PatchedBulkRackReservationRequest" } } } @@ -99707,6 +102595,22 @@ "operationId": "dcim_rack_reservations_retrieve", "description": "Get a rack reservation object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -99715,6 +102619,14 @@ }, "description": "A unique integer value identifying this rack reservation.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -99883,6 +102795,14 @@ "operationId": "dcim_rack_roles_list", "description": "Get a list of rack role objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -100284,6 +103204,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -100637,6 +103565,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -101075,7 +104011,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRoleRequest" + "$ref": "#/components/schemas/BulkRackRoleRequest" } } }, @@ -101083,7 +104019,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRoleRequest" + "$ref": "#/components/schemas/BulkRackRoleRequest" } } } @@ -101126,7 +104062,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRoleRequest" + "$ref": "#/components/schemas/PatchedBulkRackRoleRequest" } } }, @@ -101134,7 +104070,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRoleRequest" + "$ref": "#/components/schemas/PatchedBulkRackRoleRequest" } } } @@ -101212,6 +104148,22 @@ "operationId": "dcim_rack_roles_retrieve", "description": "Get a rack role object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -101220,6 +104172,14 @@ }, "description": "A unique integer value identifying this rack role.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -101388,6 +104348,14 @@ "operationId": "dcim_rack_types_list", "description": "Get a list of rack type objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -101645,6 +104613,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "form_factor", @@ -102383,6 +105359,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -103902,7 +106886,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackTypeRequest" + "$ref": "#/components/schemas/BulkRackTypeRequest" } } }, @@ -103910,7 +106894,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackTypeRequest" + "$ref": "#/components/schemas/BulkRackTypeRequest" } } } @@ -103953,7 +106937,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackTypeRequest" + "$ref": "#/components/schemas/PatchedBulkRackTypeRequest" } } }, @@ -103961,7 +106945,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackTypeRequest" + "$ref": "#/components/schemas/PatchedBulkRackTypeRequest" } } } @@ -104039,6 +107023,22 @@ "operationId": "dcim_rack_types_retrieve", "description": "Get a rack type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -104047,6 +107047,14 @@ }, "description": "A unique integer value identifying this rack type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -104523,6 +107531,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -105007,6 +108023,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "form_factor", @@ -105859,6 +108883,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -107814,7 +110846,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRequest" + "$ref": "#/components/schemas/BulkRackRequest" } } }, @@ -107822,7 +110854,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRequest" + "$ref": "#/components/schemas/BulkRackRequest" } } } @@ -107865,7 +110897,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRequest" + "$ref": "#/components/schemas/PatchedBulkRackRequest" } } }, @@ -107873,7 +110905,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RackRequest" + "$ref": "#/components/schemas/PatchedBulkRackRequest" } } } @@ -107951,6 +110983,22 @@ "operationId": "dcim_racks_retrieve", "description": "Get a rack object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -107959,6 +111007,14 @@ }, "description": "A unique integer value identifying this rack.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -108127,6 +111183,14 @@ "operationId": "dcim_racks_elevation_retrieve", "description": "Rack elevation representing the list of rack units. Also supports rendering the elevation as an SVG.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "exclude", @@ -108157,6 +111221,14 @@ }, "description": "* `front` - Front\n* `rear` - Rear" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -108208,6 +111280,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "in": "query", "name": "q", @@ -108285,6 +111365,14 @@ "operationId": "dcim_rear_port_templates_list", "description": "Get a list of rear port template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -108714,6 +111802,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "front_port_id", @@ -109272,6 +112368,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -109390,7 +112494,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109410,7 +112514,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109423,7 +112527,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109436,7 +112540,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109449,7 +112553,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109462,7 +112566,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109475,7 +112579,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109488,7 +112592,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109501,7 +112605,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109514,7 +112618,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109527,7 +112631,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109540,7 +112644,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -109653,7 +112757,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortTemplateRequest" + "$ref": "#/components/schemas/BulkRearPortTemplateRequest" } } }, @@ -109661,7 +112765,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortTemplateRequest" + "$ref": "#/components/schemas/BulkRearPortTemplateRequest" } } } @@ -109704,7 +112808,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkRearPortTemplateRequest" } } }, @@ -109712,7 +112816,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkRearPortTemplateRequest" } } } @@ -109790,6 +112894,22 @@ "operationId": "dcim_rear_port_templates_retrieve", "description": "Get a rear port template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -109798,6 +112918,14 @@ }, "description": "A unique integer value identifying this rear port template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -109966,6 +113094,14 @@ "operationId": "dcim_rear_ports_list", "description": "Get a list of rear port objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cable_connector", @@ -110965,6 +114101,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "front_port_id", @@ -111589,6 +114733,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -112113,7 +115265,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112133,7 +115285,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112146,7 +115298,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112159,7 +115311,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112172,7 +115324,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112185,7 +115337,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112198,7 +115350,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112211,7 +115363,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112224,7 +115376,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112237,7 +115389,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112250,7 +115402,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112263,7 +115415,7 @@ "type": "array", "items": { "type": "string", - "x-spec-enum-id": "2696b7065f33307c" + "x-spec-enum-id": "b64d6804afec405c" } }, "explode": true, @@ -112428,7 +115580,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortRequest" + "$ref": "#/components/schemas/BulkRearPortRequest" } } }, @@ -112436,7 +115588,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortRequest" + "$ref": "#/components/schemas/BulkRearPortRequest" } } } @@ -112479,7 +115631,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortRequest" + "$ref": "#/components/schemas/PatchedBulkRearPortRequest" } } }, @@ -112487,7 +115639,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RearPortRequest" + "$ref": "#/components/schemas/PatchedBulkRearPortRequest" } } } @@ -112565,6 +115717,22 @@ "operationId": "dcim_rear_ports_retrieve", "description": "Get a rear port object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -112573,6 +115741,14 @@ }, "description": "A unique integer value identifying this rear port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -112741,6 +115917,22 @@ "operationId": "dcim_rear_ports_paths_retrieve", "description": "Return all CablePaths which traverse a given pass-through port.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -112749,6 +115941,14 @@ }, "description": "A unique integer value identifying this rear port.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -112829,6 +116029,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -113155,6 +116363,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -113508,6 +116724,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -114000,7 +117224,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RegionRequest" + "$ref": "#/components/schemas/BulkRegionRequest" } } }, @@ -114008,7 +117232,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RegionRequest" + "$ref": "#/components/schemas/BulkRegionRequest" } } } @@ -114051,7 +117275,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RegionRequest" + "$ref": "#/components/schemas/PatchedBulkRegionRequest" } } }, @@ -114059,7 +117283,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RegionRequest" + "$ref": "#/components/schemas/PatchedBulkRegionRequest" } } } @@ -114137,6 +117361,22 @@ "operationId": "dcim_regions_retrieve", "description": "Get a region object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -114145,6 +117385,14 @@ }, "description": "A unique integer value identifying this region.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -114361,6 +117609,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -114687,6 +117943,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -115040,6 +118304,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -115532,7 +118804,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteGroupRequest" + "$ref": "#/components/schemas/BulkSiteGroupRequest" } } }, @@ -115540,7 +118812,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteGroupRequest" + "$ref": "#/components/schemas/BulkSiteGroupRequest" } } } @@ -115583,7 +118855,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteGroupRequest" + "$ref": "#/components/schemas/PatchedBulkSiteGroupRequest" } } }, @@ -115591,7 +118863,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteGroupRequest" + "$ref": "#/components/schemas/PatchedBulkSiteGroupRequest" } } } @@ -115669,6 +118941,22 @@ "operationId": "dcim_site_groups_retrieve", "description": "Get a site group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -115677,6 +118965,14 @@ }, "description": "A unique integer value identifying this site group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -115903,6 +119199,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -116380,6 +119684,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -116951,6 +120263,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -117846,7 +121166,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteRequest" + "$ref": "#/components/schemas/BulkSiteRequest" } } }, @@ -117854,7 +121174,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteRequest" + "$ref": "#/components/schemas/BulkSiteRequest" } } } @@ -117897,7 +121217,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteRequest" + "$ref": "#/components/schemas/PatchedBulkSiteRequest" } } }, @@ -117905,7 +121225,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SiteRequest" + "$ref": "#/components/schemas/PatchedBulkSiteRequest" } } } @@ -117983,6 +121303,22 @@ "operationId": "dcim_sites_retrieve", "description": "Get a site object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -117991,6 +121327,14 @@ }, "description": "A unique integer value identifying this site.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -118159,6 +121503,14 @@ "operationId": "dcim_virtual_chassis_list", "description": "Get a list of virtual chassis objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -118560,6 +121912,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -119054,6 +122414,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -119541,7 +122909,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualChassisRequest" + "$ref": "#/components/schemas/BulkVirtualChassisRequest" } } }, @@ -119549,7 +122917,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualChassisRequest" + "$ref": "#/components/schemas/BulkVirtualChassisRequest" } } } @@ -119592,7 +122960,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualChassisRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualChassisRequest" } } }, @@ -119600,7 +122968,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualChassisRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualChassisRequest" } } } @@ -119678,6 +123046,22 @@ "operationId": "dcim_virtual_chassis_retrieve", "description": "Get a virtual chassis object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -119686,6 +123070,14 @@ }, "description": "A unique integer value identifying this virtual chassis.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -119854,6 +123246,14 @@ "operationId": "dcim_virtual_device_contexts_list", "description": "Get a list of virtual device context objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -120156,6 +123556,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "has_primary_ip", @@ -120628,6 +124036,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -121284,7 +124700,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDeviceContextRequest" + "$ref": "#/components/schemas/BulkVirtualDeviceContextRequest" } } }, @@ -121292,7 +124708,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDeviceContextRequest" + "$ref": "#/components/schemas/BulkVirtualDeviceContextRequest" } } } @@ -121335,7 +124751,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDeviceContextRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualDeviceContextRequest" } } }, @@ -121343,7 +124759,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDeviceContextRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualDeviceContextRequest" } } } @@ -121421,6 +124837,22 @@ "operationId": "dcim_virtual_device_contexts_retrieve", "description": "Get a virtual device context object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -121429,6 +124861,14 @@ }, "description": "A unique integer value identifying this virtual device context.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -121597,6 +125037,14 @@ "operationId": "extras_bookmarks_list", "description": "Get a list of bookmark objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -121605,6 +125053,14 @@ "format": "date-time" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -121908,6 +125364,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -122077,7 +125541,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BookmarkRequest" + "$ref": "#/components/schemas/BulkBookmarkRequest" } } }, @@ -122085,7 +125549,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BookmarkRequest" + "$ref": "#/components/schemas/BulkBookmarkRequest" } } } @@ -122128,7 +125592,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BookmarkRequest" + "$ref": "#/components/schemas/PatchedBulkBookmarkRequest" } } }, @@ -122136,7 +125600,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BookmarkRequest" + "$ref": "#/components/schemas/PatchedBulkBookmarkRequest" } } } @@ -122214,6 +125678,22 @@ "operationId": "extras_bookmarks_retrieve", "description": "Get a bookmark object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -122222,6 +125702,14 @@ }, "description": "A unique integer value identifying this bookmark.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -122397,6 +125885,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -122788,6 +126284,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -123141,6 +126645,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -123428,7 +126940,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextProfileRequest" + "$ref": "#/components/schemas/BulkConfigContextProfileRequest" } } }, @@ -123436,7 +126948,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextProfileRequest" + "$ref": "#/components/schemas/BulkConfigContextProfileRequest" } } } @@ -123479,7 +126991,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextProfileRequest" + "$ref": "#/components/schemas/PatchedBulkConfigContextProfileRequest" } } }, @@ -123487,7 +126999,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextProfileRequest" + "$ref": "#/components/schemas/PatchedBulkConfigContextProfileRequest" } } } @@ -123565,6 +127077,22 @@ "operationId": "extras_config_context_profiles_retrieve", "description": "Get a config context profile object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -123573,6 +127101,14 @@ }, "description": "A unique integer value identifying this config context profile.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -123803,6 +127339,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cluster_group", @@ -124402,6 +127946,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -124814,6 +128366,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -125556,7 +129116,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextRequest" + "$ref": "#/components/schemas/BulkConfigContextRequest" } } }, @@ -125564,7 +129124,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextRequest" + "$ref": "#/components/schemas/BulkConfigContextRequest" } } } @@ -125607,7 +129167,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextRequest" + "$ref": "#/components/schemas/PatchedBulkConfigContextRequest" } } }, @@ -125615,7 +129175,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigContextRequest" + "$ref": "#/components/schemas/PatchedBulkConfigContextRequest" } } } @@ -125693,6 +129253,22 @@ "operationId": "extras_config_contexts_retrieve", "description": "Get a config context object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -125701,6 +129277,14 @@ }, "description": "A unique integer value identifying this config context.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -125938,6 +129522,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -126336,6 +129928,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "file_extension", @@ -127142,6 +130742,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -127429,7 +131037,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigTemplateRequest" + "$ref": "#/components/schemas/BulkConfigTemplateRequest" } } }, @@ -127437,7 +131045,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigTemplateRequest" + "$ref": "#/components/schemas/BulkConfigTemplateRequest" } } } @@ -127480,7 +131088,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkConfigTemplateRequest" } } }, @@ -127488,7 +131096,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ConfigTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkConfigTemplateRequest" } } } @@ -127566,6 +131174,22 @@ "operationId": "extras_config_templates_retrieve", "description": "Get a config template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -127574,6 +131198,14 @@ }, "description": "A unique integer value identifying this config template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -128045,6 +131677,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "choice", @@ -128318,6 +131958,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -128671,6 +132319,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "in": "query", "name": "order_alphabetically", @@ -128917,7 +132573,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldChoiceSetRequest" + "$ref": "#/components/schemas/BulkCustomFieldChoiceSetRequest" } } }, @@ -128925,7 +132581,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldChoiceSetRequest" + "$ref": "#/components/schemas/BulkCustomFieldChoiceSetRequest" } } } @@ -128968,7 +132624,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldChoiceSetRequest" + "$ref": "#/components/schemas/PatchedBulkCustomFieldChoiceSetRequest" } } }, @@ -128976,7 +132632,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldChoiceSetRequest" + "$ref": "#/components/schemas/PatchedBulkCustomFieldChoiceSetRequest" } } } @@ -129054,6 +132710,22 @@ "operationId": "extras_custom_field_choice_sets_retrieve", "description": "Get a custom field choice set object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -129062,6 +132734,14 @@ }, "description": "A unique integer value identifying this custom field choice set.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -129230,6 +132910,22 @@ "operationId": "extras_custom_field_choice_sets_choices_retrieve", "description": "Provides an endpoint to iterate through each choice in a set.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -129238,6 +132934,14 @@ }, "description": "A unique integer value identifying this custom field choice set.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -129270,6 +132974,14 @@ "operationId": "extras_custom_fields_list", "description": "Get a list of custom field objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "choice_set", @@ -129570,6 +133282,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "filter_logic", @@ -130557,6 +134277,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -131838,7 +135566,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldRequest" + "$ref": "#/components/schemas/BulkCustomFieldRequest" } } }, @@ -131846,7 +135574,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldRequest" + "$ref": "#/components/schemas/BulkCustomFieldRequest" } } } @@ -131889,7 +135617,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldRequest" + "$ref": "#/components/schemas/PatchedBulkCustomFieldRequest" } } }, @@ -131897,7 +135625,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomFieldRequest" + "$ref": "#/components/schemas/PatchedBulkCustomFieldRequest" } } } @@ -131975,6 +135703,22 @@ "operationId": "extras_custom_fields_retrieve", "description": "Get a custom field object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -131983,6 +135727,14 @@ }, "description": "A unique integer value identifying this custom field.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -132151,6 +135903,14 @@ "operationId": "extras_custom_links_list", "description": "Get a list of custom link objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "button_class", @@ -132438,6 +136198,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group_name", @@ -133285,6 +137053,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -133609,7 +137385,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomLinkRequest" + "$ref": "#/components/schemas/BulkCustomLinkRequest" } } }, @@ -133617,7 +137393,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomLinkRequest" + "$ref": "#/components/schemas/BulkCustomLinkRequest" } } } @@ -133660,7 +137436,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomLinkRequest" + "$ref": "#/components/schemas/PatchedBulkCustomLinkRequest" } } }, @@ -133668,7 +137444,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/CustomLinkRequest" + "$ref": "#/components/schemas/PatchedBulkCustomLinkRequest" } } } @@ -133746,6 +137522,22 @@ "operationId": "extras_custom_links_retrieve", "description": "Get a custom link object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -133754,6 +137546,14 @@ }, "description": "A unique integer value identifying this custom link.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -133921,6 +137721,32 @@ "get": { "operationId": "extras_dashboard_retrieve", "description": "Get a list of dashboard objects.", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "extras" ], @@ -134331,6 +138157,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -134600,6 +138434,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -135121,6 +138963,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -135408,7 +139258,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/EventRuleRequest" + "$ref": "#/components/schemas/BulkEventRuleRequest" } } }, @@ -135416,7 +139266,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/EventRuleRequest" + "$ref": "#/components/schemas/BulkEventRuleRequest" } } } @@ -135459,7 +139309,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/EventRuleRequest" + "$ref": "#/components/schemas/PatchedBulkEventRuleRequest" } } }, @@ -135467,7 +139317,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/EventRuleRequest" + "$ref": "#/components/schemas/PatchedBulkEventRuleRequest" } } } @@ -135545,6 +139395,22 @@ "operationId": "extras_event_rules_retrieve", "description": "Get a event rule object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -135553,6 +139419,14 @@ }, "description": "A unique integer value identifying this event rule.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -135735,6 +139609,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -136126,6 +140008,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "file_extension", @@ -137100,6 +140990,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -137339,7 +141237,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ExportTemplateRequest" + "$ref": "#/components/schemas/BulkExportTemplateRequest" } } }, @@ -137347,7 +141245,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ExportTemplateRequest" + "$ref": "#/components/schemas/BulkExportTemplateRequest" } } } @@ -137390,7 +141288,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ExportTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkExportTemplateRequest" } } }, @@ -137398,7 +141296,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ExportTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkExportTemplateRequest" } } } @@ -137476,6 +141374,22 @@ "operationId": "extras_export_templates_retrieve", "description": "Get a export template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -137484,6 +141398,14 @@ }, "description": "A unique integer value identifying this export template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -137707,6 +141629,14 @@ "operationId": "extras_image_attachments_list", "description": "Get a list of image attachment objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -137957,6 +141887,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -138603,6 +142541,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -138736,7 +142682,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ImageAttachmentRequest" + "$ref": "#/components/schemas/BulkImageAttachmentRequest" } } }, @@ -138744,7 +142690,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ImageAttachmentRequest" + "$ref": "#/components/schemas/BulkImageAttachmentRequest" } } } @@ -138787,7 +142733,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ImageAttachmentRequest" + "$ref": "#/components/schemas/PatchedBulkImageAttachmentRequest" } } }, @@ -138795,7 +142741,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ImageAttachmentRequest" + "$ref": "#/components/schemas/PatchedBulkImageAttachmentRequest" } } } @@ -138873,6 +142819,22 @@ "operationId": "extras_image_attachments_retrieve", "description": "Get a image attachment object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -138881,6 +142843,14 @@ }, "description": "A unique integer value identifying this image attachment.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -139182,6 +143152,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created_after", @@ -139260,6 +143238,14 @@ "format": "uuid" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -139625,6 +143611,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -139806,7 +143800,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/JournalEntryRequest" + "$ref": "#/components/schemas/BulkJournalEntryRequest" } } }, @@ -139814,7 +143808,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/JournalEntryRequest" + "$ref": "#/components/schemas/BulkJournalEntryRequest" } } } @@ -139857,7 +143851,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/JournalEntryRequest" + "$ref": "#/components/schemas/PatchedBulkJournalEntryRequest" } } }, @@ -139865,7 +143859,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/JournalEntryRequest" + "$ref": "#/components/schemas/PatchedBulkJournalEntryRequest" } } } @@ -139943,6 +143937,22 @@ "operationId": "extras_journal_entries_retrieve", "description": "Get a journal entry object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -139951,6 +143961,14 @@ }, "description": "A unique integer value identifying this journal entry.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -140119,6 +144137,22 @@ "operationId": "extras_notification_groups_list", "description": "Get a list of notification group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "name": "limit", "required": false, @@ -140137,6 +144171,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -140254,7 +144296,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationGroupRequest" + "$ref": "#/components/schemas/BulkNotificationGroupRequest" } } }, @@ -140262,7 +144304,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationGroupRequest" + "$ref": "#/components/schemas/BulkNotificationGroupRequest" } } } @@ -140305,7 +144347,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationGroupRequest" + "$ref": "#/components/schemas/PatchedBulkNotificationGroupRequest" } } }, @@ -140313,7 +144355,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationGroupRequest" + "$ref": "#/components/schemas/PatchedBulkNotificationGroupRequest" } } } @@ -140391,6 +144433,22 @@ "operationId": "extras_notification_groups_retrieve", "description": "Get a notification group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -140399,6 +144457,14 @@ }, "description": "A unique integer value identifying this notification group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -140567,6 +144633,22 @@ "operationId": "extras_notifications_list", "description": "Get a list of notification objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "name": "limit", "required": false, @@ -140585,6 +144667,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -140702,7 +144792,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationRequest" + "$ref": "#/components/schemas/BulkNotificationRequest" } } }, @@ -140710,7 +144800,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationRequest" + "$ref": "#/components/schemas/BulkNotificationRequest" } } } @@ -140753,7 +144843,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationRequest" + "$ref": "#/components/schemas/PatchedBulkNotificationRequest" } } }, @@ -140761,7 +144851,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationRequest" + "$ref": "#/components/schemas/PatchedBulkNotificationRequest" } } } @@ -140839,6 +144929,22 @@ "operationId": "extras_notifications_retrieve", "description": "Get a notification object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -140847,6 +144953,14 @@ }, "description": "A unique integer value identifying this notification.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -141015,6 +145129,14 @@ "operationId": "extras_saved_filters_list", "description": "Get a list of saved filter objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -141272,6 +145394,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -141793,6 +145923,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -142336,7 +146474,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SavedFilterRequest" + "$ref": "#/components/schemas/BulkSavedFilterRequest" } } }, @@ -142344,7 +146482,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SavedFilterRequest" + "$ref": "#/components/schemas/BulkSavedFilterRequest" } } } @@ -142387,7 +146525,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SavedFilterRequest" + "$ref": "#/components/schemas/PatchedBulkSavedFilterRequest" } } }, @@ -142395,7 +146533,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SavedFilterRequest" + "$ref": "#/components/schemas/PatchedBulkSavedFilterRequest" } } } @@ -142473,6 +146611,22 @@ "operationId": "extras_saved_filters_retrieve", "description": "Get a saved filter object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -142481,6 +146635,14 @@ }, "description": "A unique integer value identifying this saved filter.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -142649,6 +146811,22 @@ "operationId": "extras_scripts_list", "description": "Get a list of script objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -142936,6 +147114,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -143020,6 +147206,22 @@ "operationId": "extras_scripts_retrieve", "description": "Get a script object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -143028,6 +147230,14 @@ "pattern": "^[^/]+$" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -143240,6 +147450,22 @@ "operationId": "extras_subscriptions_list", "description": "Get a list of subscription objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "name": "limit", "required": false, @@ -143258,6 +147484,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -143375,7 +147609,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SubscriptionRequest" + "$ref": "#/components/schemas/BulkSubscriptionRequest" } } }, @@ -143383,7 +147617,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SubscriptionRequest" + "$ref": "#/components/schemas/BulkSubscriptionRequest" } } } @@ -143426,7 +147660,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SubscriptionRequest" + "$ref": "#/components/schemas/PatchedBulkSubscriptionRequest" } } }, @@ -143434,7 +147668,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/SubscriptionRequest" + "$ref": "#/components/schemas/PatchedBulkSubscriptionRequest" } } } @@ -143512,6 +147746,22 @@ "operationId": "extras_subscriptions_retrieve", "description": "Get a subscription object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -143520,6 +147770,14 @@ }, "description": "A unique integer value identifying this subscription.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -143688,6 +147946,14 @@ "operationId": "extras_table_configs_list", "description": "Get a list of table config objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -143945,6 +148211,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -144346,6 +148620,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -144783,7 +149065,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TableConfigRequest" + "$ref": "#/components/schemas/BulkTableConfigRequest" } } }, @@ -144791,7 +149073,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TableConfigRequest" + "$ref": "#/components/schemas/BulkTableConfigRequest" } } } @@ -144834,7 +149116,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TableConfigRequest" + "$ref": "#/components/schemas/PatchedBulkTableConfigRequest" } } }, @@ -144842,7 +149124,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TableConfigRequest" + "$ref": "#/components/schemas/PatchedBulkTableConfigRequest" } } } @@ -144920,6 +149202,22 @@ "operationId": "extras_table_configs_retrieve", "description": "Get a table config object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -144928,6 +149226,14 @@ }, "description": "A unique integer value identifying this table config.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -145096,6 +149402,22 @@ "operationId": "extras_tagged_objects_list", "description": "Get a list of tagged item objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -145332,6 +149654,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -145437,6 +149767,22 @@ "operationId": "extras_tagged_objects_retrieve", "description": "Get a tagged item object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -145445,6 +149791,14 @@ }, "description": "A unique integer value identifying this tagged item.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -145477,6 +149831,14 @@ "operationId": "extras_tags_list", "description": "Get a list of tag objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "color", @@ -145903,6 +150265,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "for_object_type_id", @@ -146293,6 +150663,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -146768,7 +151146,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TagRequest" + "$ref": "#/components/schemas/BulkTagRequest" } } }, @@ -146776,7 +151154,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TagRequest" + "$ref": "#/components/schemas/BulkTagRequest" } } } @@ -146819,7 +151197,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TagRequest" + "$ref": "#/components/schemas/PatchedBulkTagRequest" } } }, @@ -146827,7 +151205,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TagRequest" + "$ref": "#/components/schemas/PatchedBulkTagRequest" } } } @@ -146905,6 +151283,22 @@ "operationId": "extras_tags_retrieve", "description": "Get a tag object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -146913,6 +151307,14 @@ }, "description": "A unique integer value identifying this tag.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -147081,6 +151483,14 @@ "operationId": "extras_webhooks_list", "description": "Get a list of webhook objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "ca_file_path", @@ -147482,6 +151892,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "http_content_type", @@ -148149,6 +152567,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -148606,7 +153032,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WebhookRequest" + "$ref": "#/components/schemas/BulkWebhookRequest" } } }, @@ -148614,7 +153040,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WebhookRequest" + "$ref": "#/components/schemas/BulkWebhookRequest" } } } @@ -148657,7 +153083,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WebhookRequest" + "$ref": "#/components/schemas/PatchedBulkWebhookRequest" } } }, @@ -148665,7 +153091,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WebhookRequest" + "$ref": "#/components/schemas/PatchedBulkWebhookRequest" } } } @@ -148743,6 +153169,22 @@ "operationId": "extras_webhooks_retrieve", "description": "Get a webhook object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -148751,6 +153193,14 @@ }, "description": "A unique integer value identifying this webhook.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -148919,6 +153369,14 @@ "operationId": "ipam_aggregates_list", "description": "Get a list of aggregate objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -149337,6 +153795,14 @@ "type": "number" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -149539,6 +154005,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -149988,7 +154462,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/AggregateRequest" + "$ref": "#/components/schemas/BulkAggregateRequest" } } }, @@ -149996,7 +154470,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/AggregateRequest" + "$ref": "#/components/schemas/BulkAggregateRequest" } } } @@ -150039,7 +154513,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/AggregateRequest" + "$ref": "#/components/schemas/PatchedBulkAggregateRequest" } } }, @@ -150047,7 +154521,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/AggregateRequest" + "$ref": "#/components/schemas/PatchedBulkAggregateRequest" } } } @@ -150125,6 +154599,22 @@ "operationId": "ipam_aggregates_retrieve", "description": "Get a aggregate object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -150133,6 +154623,14 @@ }, "description": "A unique integer value identifying this aggregate.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -150301,6 +154799,14 @@ "operationId": "ipam_asn_ranges_list", "description": "Get a list of ASN range objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -150636,6 +155142,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -150989,6 +155503,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -151653,7 +156175,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRangeRequest" + "$ref": "#/components/schemas/BulkASNRangeRequest" } } }, @@ -151661,7 +156183,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRangeRequest" + "$ref": "#/components/schemas/BulkASNRangeRequest" } } } @@ -151704,7 +156226,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRangeRequest" + "$ref": "#/components/schemas/PatchedBulkASNRangeRequest" } } }, @@ -151712,7 +156234,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRangeRequest" + "$ref": "#/components/schemas/PatchedBulkASNRangeRequest" } } } @@ -151790,6 +156312,22 @@ "operationId": "ipam_asn_ranges_retrieve", "description": "Get a ASN range object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -151798,6 +156336,14 @@ }, "description": "A unique integer value identifying this ASN range.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -151966,6 +156512,22 @@ "operationId": "ipam_asn_ranges_available_asns_list", "description": "Get a ASN object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -151973,6 +156535,14 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -152154,6 +156724,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -152404,6 +156982,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -152606,6 +157192,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -153253,7 +157847,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRequest" + "$ref": "#/components/schemas/BulkASNRequest" } } }, @@ -153261,7 +157855,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRequest" + "$ref": "#/components/schemas/BulkASNRequest" } } } @@ -153304,7 +157898,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRequest" + "$ref": "#/components/schemas/PatchedBulkASNRequest" } } }, @@ -153312,7 +157906,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ASNRequest" + "$ref": "#/components/schemas/PatchedBulkASNRequest" } } } @@ -153390,6 +157984,22 @@ "operationId": "ipam_asns_retrieve", "description": "Get a ASN object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -153398,6 +158008,14 @@ }, "description": "A unique integer value identifying this ASN.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -153566,6 +158184,14 @@ "operationId": "ipam_fhrp_group_assignments_list", "description": "Get a list of FHRP group assignment objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -153690,6 +158316,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group_id", @@ -154027,6 +158661,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -154262,7 +158904,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupAssignmentRequest" + "$ref": "#/components/schemas/BulkFHRPGroupAssignmentRequest" } } }, @@ -154270,7 +158912,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupAssignmentRequest" + "$ref": "#/components/schemas/BulkFHRPGroupAssignmentRequest" } } } @@ -154313,7 +158955,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupAssignmentRequest" + "$ref": "#/components/schemas/PatchedBulkFHRPGroupAssignmentRequest" } } }, @@ -154321,7 +158963,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupAssignmentRequest" + "$ref": "#/components/schemas/PatchedBulkFHRPGroupAssignmentRequest" } } } @@ -154399,6 +159041,22 @@ "operationId": "ipam_fhrp_group_assignments_retrieve", "description": "Get a FHRP group assignment object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -154407,6 +159065,14 @@ }, "description": "A unique integer value identifying this FHRP group assignment.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -154913,6 +159579,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -155163,6 +159837,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group_id", @@ -155601,6 +160283,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -156063,7 +160753,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupRequest" + "$ref": "#/components/schemas/BulkFHRPGroupRequest" } } }, @@ -156071,7 +160761,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupRequest" + "$ref": "#/components/schemas/BulkFHRPGroupRequest" } } } @@ -156114,7 +160804,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupRequest" + "$ref": "#/components/schemas/PatchedBulkFHRPGroupRequest" } } }, @@ -156122,7 +160812,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FHRPGroupRequest" + "$ref": "#/components/schemas/PatchedBulkFHRPGroupRequest" } } } @@ -156200,6 +160890,22 @@ "operationId": "ipam_fhrp_groups_retrieve", "description": "Get a FHRP group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -156208,6 +160914,14 @@ }, "description": "A unique integer value identifying this FHRP group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -156513,6 +161227,14 @@ }, "description": "Is assigned to an interface" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -157048,6 +161770,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -157355,6 +162085,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -158293,7 +163031,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPAddressRequest" + "$ref": "#/components/schemas/BulkIPAddressRequest" } } }, @@ -158301,7 +163039,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPAddressRequest" + "$ref": "#/components/schemas/BulkIPAddressRequest" } } } @@ -158344,7 +163082,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPAddressRequest" + "$ref": "#/components/schemas/PatchedBulkIPAddressRequest" } } }, @@ -158352,7 +163090,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPAddressRequest" + "$ref": "#/components/schemas/PatchedBulkIPAddressRequest" } } } @@ -158430,6 +163168,22 @@ "operationId": "ipam_ip_addresses_retrieve", "description": "Get a IP address object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -158438,6 +163192,14 @@ }, "description": "A unique integer value identifying this IP address.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -158606,6 +163368,14 @@ "operationId": "ipam_ip_ranges_list", "description": "Get a list of IP range objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -158959,6 +163729,14 @@ "type": "number" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -159175,6 +163953,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -159960,7 +164746,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPRangeRequest" + "$ref": "#/components/schemas/BulkIPRangeRequest" } } }, @@ -159968,7 +164754,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPRangeRequest" + "$ref": "#/components/schemas/BulkIPRangeRequest" } } } @@ -160011,7 +164797,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPRangeRequest" + "$ref": "#/components/schemas/PatchedBulkIPRangeRequest" } } }, @@ -160019,7 +164805,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPRangeRequest" + "$ref": "#/components/schemas/PatchedBulkIPRangeRequest" } } } @@ -160097,6 +164883,22 @@ "operationId": "ipam_ip_ranges_retrieve", "description": "Get a IP range object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -160105,6 +164907,14 @@ }, "description": "A unique integer value identifying this IP range.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -160273,6 +165083,22 @@ "operationId": "ipam_ip_ranges_available_ips_list", "description": "Get a IP address object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -160280,6 +165106,14 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -160376,6 +165210,14 @@ "operationId": "ipam_prefixes_list", "description": "Get a list of prefix objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "children", @@ -160899,6 +165741,14 @@ "type": "number" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -161190,6 +166040,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -162301,7 +167159,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PrefixRequest" + "$ref": "#/components/schemas/BulkPrefixRequest" } } }, @@ -162309,7 +167167,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PrefixRequest" + "$ref": "#/components/schemas/BulkPrefixRequest" } } } @@ -162352,7 +167210,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PrefixRequest" + "$ref": "#/components/schemas/PatchedBulkPrefixRequest" } } }, @@ -162360,7 +167218,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PrefixRequest" + "$ref": "#/components/schemas/PatchedBulkPrefixRequest" } } } @@ -162438,6 +167296,22 @@ "operationId": "ipam_prefixes_retrieve", "description": "Get a prefix object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -162446,6 +167320,14 @@ }, "description": "A unique integer value identifying this prefix.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -162614,6 +167496,22 @@ "operationId": "ipam_prefixes_available_ips_list", "description": "Get a IP address object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -162621,6 +167519,14 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -162717,6 +167623,22 @@ "operationId": "ipam_prefixes_available_prefixes_list", "description": "Get a prefix object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -162724,6 +167646,14 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -162820,6 +167750,14 @@ "operationId": "ipam_rirs_list", "description": "Get a list of RIR objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -163070,6 +168008,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -163430,6 +168376,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -163868,7 +168822,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RIRRequest" + "$ref": "#/components/schemas/BulkRIRRequest" } } }, @@ -163876,7 +168830,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RIRRequest" + "$ref": "#/components/schemas/BulkRIRRequest" } } } @@ -163919,7 +168873,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RIRRequest" + "$ref": "#/components/schemas/PatchedBulkRIRRequest" } } }, @@ -163927,7 +168881,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RIRRequest" + "$ref": "#/components/schemas/PatchedBulkRIRRequest" } } } @@ -164005,6 +168959,22 @@ "operationId": "ipam_rirs_retrieve", "description": "Get a RIR object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -164013,6 +168983,14 @@ }, "description": "A unique integer value identifying this RIR.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -164181,6 +169159,14 @@ "operationId": "ipam_roles_list", "description": "Get a list of role objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -164431,6 +169417,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -164784,6 +169778,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -165307,7 +170309,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RoleRequest" + "$ref": "#/components/schemas/BulkRoleRequest" } } }, @@ -165315,7 +170317,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RoleRequest" + "$ref": "#/components/schemas/BulkRoleRequest" } } } @@ -165358,7 +170360,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RoleRequest" + "$ref": "#/components/schemas/PatchedBulkRoleRequest" } } }, @@ -165366,7 +170368,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RoleRequest" + "$ref": "#/components/schemas/PatchedBulkRoleRequest" } } } @@ -165444,6 +170446,22 @@ "operationId": "ipam_roles_retrieve", "description": "Get a role object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -165452,6 +170470,14 @@ }, "description": "A unique integer value identifying this role.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -165620,6 +170646,14 @@ "operationId": "ipam_route_targets_list", "description": "Get a list of route target objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -165986,6 +171020,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -166455,6 +171497,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -166844,7 +171894,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RouteTargetRequest" + "$ref": "#/components/schemas/BulkRouteTargetRequest" } } }, @@ -166852,7 +171902,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RouteTargetRequest" + "$ref": "#/components/schemas/BulkRouteTargetRequest" } } } @@ -166895,7 +171945,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RouteTargetRequest" + "$ref": "#/components/schemas/PatchedBulkRouteTargetRequest" } } }, @@ -166903,7 +171953,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/RouteTargetRequest" + "$ref": "#/components/schemas/PatchedBulkRouteTargetRequest" } } } @@ -166981,6 +172031,22 @@ "operationId": "ipam_route_targets_retrieve", "description": "Get a route target object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -166989,6 +172055,14 @@ }, "description": "A unique integer value identifying this route target.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -167157,6 +172231,14 @@ "operationId": "ipam_service_templates_list", "description": "Get a list of application service template objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -167407,6 +172489,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -167760,6 +172850,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -168253,7 +173351,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceTemplateRequest" + "$ref": "#/components/schemas/BulkServiceTemplateRequest" } } }, @@ -168261,7 +173359,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceTemplateRequest" + "$ref": "#/components/schemas/BulkServiceTemplateRequest" } } } @@ -168304,7 +173402,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkServiceTemplateRequest" } } }, @@ -168312,7 +173410,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceTemplateRequest" + "$ref": "#/components/schemas/PatchedBulkServiceTemplateRequest" } } } @@ -168390,6 +173488,22 @@ "operationId": "ipam_service_templates_retrieve", "description": "Get a application service template object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -168398,6 +173512,14 @@ }, "description": "A unique integer value identifying this application service template.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -168566,6 +173688,14 @@ "operationId": "ipam_services_list", "description": "Get a list of application service objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -168942,6 +174072,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -169347,6 +174485,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -169974,7 +175120,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceRequest" + "$ref": "#/components/schemas/BulkServiceRequest" } } }, @@ -169982,7 +175128,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceRequest" + "$ref": "#/components/schemas/BulkServiceRequest" } } } @@ -170025,7 +175171,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceRequest" + "$ref": "#/components/schemas/PatchedBulkServiceRequest" } } }, @@ -170033,7 +175179,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceRequest" + "$ref": "#/components/schemas/PatchedBulkServiceRequest" } } } @@ -170111,6 +175257,22 @@ "operationId": "ipam_services_retrieve", "description": "Get a application service object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -170119,6 +175281,14 @@ }, "description": "A unique integer value identifying this application service.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -170287,6 +175457,14 @@ "operationId": "ipam_vlan_groups_list", "description": "Get a list of VLAN group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cluster", @@ -170558,6 +175736,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -170918,6 +176104,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -171687,7 +176881,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANGroupRequest" + "$ref": "#/components/schemas/BulkVLANGroupRequest" } } }, @@ -171695,7 +176889,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANGroupRequest" + "$ref": "#/components/schemas/BulkVLANGroupRequest" } } } @@ -171738,7 +176932,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANGroupRequest" + "$ref": "#/components/schemas/PatchedBulkVLANGroupRequest" } } }, @@ -171746,7 +176940,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANGroupRequest" + "$ref": "#/components/schemas/PatchedBulkVLANGroupRequest" } } } @@ -171824,6 +177018,22 @@ "operationId": "ipam_vlan_groups_retrieve", "description": "Get a VLAN group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -171832,6 +177042,14 @@ }, "description": "A unique integer value identifying this VLAN group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -172000,6 +177218,22 @@ "operationId": "ipam_vlan_groups_available_vlans_list", "description": "Get a VLAN object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -172007,6 +177241,14 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -172103,6 +177345,14 @@ "operationId": "ipam_vlan_translation_policies_list", "description": "Get a list of VLAN translation policy objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -172353,6 +177603,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -172706,6 +177964,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -172993,7 +178259,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationPolicyRequest" + "$ref": "#/components/schemas/BulkVLANTranslationPolicyRequest" } } }, @@ -173001,7 +178267,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationPolicyRequest" + "$ref": "#/components/schemas/BulkVLANTranslationPolicyRequest" } } } @@ -173044,7 +178310,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationPolicyRequest" + "$ref": "#/components/schemas/PatchedBulkVLANTranslationPolicyRequest" } } }, @@ -173052,7 +178318,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationPolicyRequest" + "$ref": "#/components/schemas/PatchedBulkVLANTranslationPolicyRequest" } } } @@ -173130,6 +178396,22 @@ "operationId": "ipam_vlan_translation_policies_retrieve", "description": "Get a VLAN translation policy object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -173138,6 +178420,14 @@ }, "description": "A unique integer value identifying this VLAN translation policy.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -173306,6 +178596,14 @@ "operationId": "ipam_vlan_translation_rules_list", "description": "Get a list of VLAN translation rule objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -173556,6 +178854,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -173843,6 +179149,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -174161,7 +179475,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationRuleRequest" + "$ref": "#/components/schemas/BulkVLANTranslationRuleRequest" } } }, @@ -174169,7 +179483,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationRuleRequest" + "$ref": "#/components/schemas/BulkVLANTranslationRuleRequest" } } } @@ -174212,7 +179526,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationRuleRequest" + "$ref": "#/components/schemas/PatchedBulkVLANTranslationRuleRequest" } } }, @@ -174220,7 +179534,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANTranslationRuleRequest" + "$ref": "#/components/schemas/PatchedBulkVLANTranslationRuleRequest" } } } @@ -174298,6 +179612,22 @@ "operationId": "ipam_vlan_translation_rules_retrieve", "description": "Get a VLAN translation rule object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -174306,6 +179636,14 @@ }, "description": "A unique integer value identifying this VLAN translation rule.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -174495,6 +179833,14 @@ "type": "string" } }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -174745,6 +180091,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -175219,6 +180573,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -176397,7 +181759,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANRequest" + "$ref": "#/components/schemas/BulkVLANRequest" } } }, @@ -176405,7 +181767,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANRequest" + "$ref": "#/components/schemas/BulkVLANRequest" } } } @@ -176448,7 +181810,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANRequest" + "$ref": "#/components/schemas/PatchedBulkVLANRequest" } } }, @@ -176456,7 +181818,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VLANRequest" + "$ref": "#/components/schemas/PatchedBulkVLANRequest" } } } @@ -176534,6 +181896,22 @@ "operationId": "ipam_vlans_retrieve", "description": "Get a VLAN object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -176542,6 +181920,14 @@ }, "description": "A unique integer value identifying this VLAN.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -176710,6 +182096,14 @@ "operationId": "ipam_vrfs_list", "description": "Get a list of VRF objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -177019,6 +182413,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -177424,6 +182826,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -177964,7 +183374,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VRFRequest" + "$ref": "#/components/schemas/BulkVRFRequest" } } }, @@ -177972,7 +183382,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VRFRequest" + "$ref": "#/components/schemas/BulkVRFRequest" } } } @@ -178015,7 +183425,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VRFRequest" + "$ref": "#/components/schemas/PatchedBulkVRFRequest" } } }, @@ -178023,7 +183433,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VRFRequest" + "$ref": "#/components/schemas/PatchedBulkVRFRequest" } } } @@ -178101,6 +183511,22 @@ "operationId": "ipam_vrfs_retrieve", "description": "Get a VRF object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -178109,6 +183535,14 @@ }, "description": "A unique integer value identifying this VRF.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -178277,6 +183711,22 @@ "operationId": "schema_retrieve", "description": "OpenApi3 schema for this API. Format can be selected via content negotiation.\n\n- YAML: application/vnd.oai.openapi\n- JSON: application/vnd.oai.openapi+json", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "format", @@ -178312,6 +183762,14 @@ "zh" ] } + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -178363,6 +183821,32 @@ "get": { "operationId": "status_retrieve", "description": "A lightweight read-only endpoint for conveying NetBox's current operational status.", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "status" ], @@ -178394,6 +183878,14 @@ "operationId": "tenancy_contact_assignments_list", "description": "Get a list of contact assignment objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact_id", @@ -178519,6 +184011,14 @@ "format": "uuid" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -178892,6 +184392,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -179286,7 +184794,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactAssignmentRequest" + "$ref": "#/components/schemas/BulkContactAssignmentRequest" } } }, @@ -179294,7 +184802,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactAssignmentRequest" + "$ref": "#/components/schemas/BulkContactAssignmentRequest" } } } @@ -179337,7 +184845,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactAssignmentRequest" + "$ref": "#/components/schemas/PatchedBulkContactAssignmentRequest" } } }, @@ -179345,7 +184853,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactAssignmentRequest" + "$ref": "#/components/schemas/PatchedBulkContactAssignmentRequest" } } } @@ -179423,6 +184931,22 @@ "operationId": "tenancy_contact_assignments_retrieve", "description": "Get a contact assignment object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -179431,6 +184955,14 @@ }, "description": "A unique integer value identifying this contact assignment.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -179647,6 +185179,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact_id", @@ -179923,6 +185463,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -180276,6 +185824,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -180768,7 +186324,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactGroupRequest" + "$ref": "#/components/schemas/BulkContactGroupRequest" } } }, @@ -180776,7 +186332,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactGroupRequest" + "$ref": "#/components/schemas/BulkContactGroupRequest" } } } @@ -180819,7 +186375,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactGroupRequest" + "$ref": "#/components/schemas/PatchedBulkContactGroupRequest" } } }, @@ -180827,7 +186383,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactGroupRequest" + "$ref": "#/components/schemas/PatchedBulkContactGroupRequest" } } } @@ -180905,6 +186461,22 @@ "operationId": "tenancy_contact_groups_retrieve", "description": "Get a contact group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -180913,6 +186485,14 @@ }, "description": "A unique integer value identifying this contact group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -181081,6 +186661,14 @@ "operationId": "tenancy_contact_roles_list", "description": "Get a list of contact role objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -181331,6 +186919,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -181684,6 +187280,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -182122,7 +187726,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRoleRequest" + "$ref": "#/components/schemas/BulkContactRoleRequest" } } }, @@ -182130,7 +187734,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRoleRequest" + "$ref": "#/components/schemas/BulkContactRoleRequest" } } } @@ -182173,7 +187777,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRoleRequest" + "$ref": "#/components/schemas/PatchedBulkContactRoleRequest" } } }, @@ -182181,7 +187785,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRoleRequest" + "$ref": "#/components/schemas/PatchedBulkContactRoleRequest" } } } @@ -182259,6 +187863,22 @@ "operationId": "tenancy_contact_roles_retrieve", "description": "Get a contact role object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -182267,6 +187887,14 @@ }, "description": "A unique integer value identifying this contact role.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -182586,6 +188214,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -182987,6 +188623,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -183539,6 +189183,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -184128,7 +189780,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRequest" + "$ref": "#/components/schemas/BulkContactRequest" } } }, @@ -184136,7 +189788,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRequest" + "$ref": "#/components/schemas/BulkContactRequest" } } } @@ -184179,7 +189831,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRequest" + "$ref": "#/components/schemas/PatchedBulkContactRequest" } } }, @@ -184187,7 +189839,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ContactRequest" + "$ref": "#/components/schemas/PatchedBulkContactRequest" } } } @@ -184265,6 +189917,22 @@ "operationId": "tenancy_contacts_retrieve", "description": "Get a contact object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -184273,6 +189941,14 @@ }, "description": "A unique integer value identifying this contact.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -184489,6 +190165,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -184739,6 +190423,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -185092,6 +190784,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -185584,7 +191284,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantGroupRequest" + "$ref": "#/components/schemas/BulkTenantGroupRequest" } } }, @@ -185592,7 +191292,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantGroupRequest" + "$ref": "#/components/schemas/BulkTenantGroupRequest" } } } @@ -185635,7 +191335,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantGroupRequest" + "$ref": "#/components/schemas/PatchedBulkTenantGroupRequest" } } }, @@ -185643,7 +191343,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantGroupRequest" + "$ref": "#/components/schemas/PatchedBulkTenantGroupRequest" } } } @@ -185721,6 +191421,22 @@ "operationId": "tenancy_tenant_groups_retrieve", "description": "Get a tenant group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -185729,6 +191445,14 @@ }, "description": "A unique integer value identifying this tenant group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -185897,6 +191621,14 @@ "operationId": "tenancy_tenants_list", "description": "Get a list of tenant objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -186223,6 +191955,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -186624,6 +192364,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -187062,7 +192810,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantRequest" + "$ref": "#/components/schemas/BulkTenantRequest" } } }, @@ -187070,7 +192818,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantRequest" + "$ref": "#/components/schemas/BulkTenantRequest" } } } @@ -187113,7 +192861,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantRequest" + "$ref": "#/components/schemas/PatchedBulkTenantRequest" } } }, @@ -187121,7 +192869,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TenantRequest" + "$ref": "#/components/schemas/PatchedBulkTenantRequest" } } } @@ -187199,6 +192947,22 @@ "operationId": "tenancy_tenants_retrieve", "description": "Get a tenant object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -187207,6 +192971,14 @@ }, "description": "A unique integer value identifying this tenant.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -187374,6 +193146,32 @@ "get": { "operationId": "users_config_retrieve", "description": "An API endpoint via which a user can update his or her own UserConfig data (but no one else's).", + "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + } + ], "tags": [ "users" ], @@ -187405,6 +193203,14 @@ "operationId": "users_groups_list", "description": "Get a list of group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "description", @@ -187556,6 +193362,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -187836,6 +193650,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -188065,7 +193887,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/GroupRequest" + "$ref": "#/components/schemas/BulkGroupRequest" } } }, @@ -188073,7 +193895,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/GroupRequest" + "$ref": "#/components/schemas/BulkGroupRequest" } } } @@ -188116,7 +193938,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/GroupRequest" + "$ref": "#/components/schemas/PatchedBulkGroupRequest" } } }, @@ -188124,7 +193946,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/GroupRequest" + "$ref": "#/components/schemas/PatchedBulkGroupRequest" } } } @@ -188202,6 +194024,22 @@ "operationId": "users_groups_retrieve", "description": "Get a group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -188210,6 +194048,14 @@ }, "description": "A unique integer value identifying this group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -188378,6 +194224,14 @@ "operationId": "users_owner_groups_list", "description": "Get a list of owner group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "description", @@ -188529,6 +194383,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -188783,6 +194645,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -188908,7 +194778,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerGroupRequest" + "$ref": "#/components/schemas/BulkOwnerGroupRequest" } } }, @@ -188916,7 +194786,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerGroupRequest" + "$ref": "#/components/schemas/BulkOwnerGroupRequest" } } } @@ -188959,7 +194829,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerGroupRequest" + "$ref": "#/components/schemas/PatchedBulkOwnerGroupRequest" } } }, @@ -188967,7 +194837,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerGroupRequest" + "$ref": "#/components/schemas/PatchedBulkOwnerGroupRequest" } } } @@ -189045,6 +194915,22 @@ "operationId": "users_owner_groups_retrieve", "description": "Get a owner group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -189053,6 +194939,14 @@ }, "description": "A unique integer value identifying this owner group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -189221,6 +195115,14 @@ "operationId": "users_owners_list", "description": "Get a list of owner objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "description", @@ -189372,6 +195274,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -189680,6 +195590,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -189909,7 +195827,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerRequest" + "$ref": "#/components/schemas/BulkOwnerRequest" } } }, @@ -189917,7 +195835,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerRequest" + "$ref": "#/components/schemas/BulkOwnerRequest" } } } @@ -189960,7 +195878,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerRequest" + "$ref": "#/components/schemas/PatchedBulkOwnerRequest" } } }, @@ -189968,7 +195886,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/OwnerRequest" + "$ref": "#/components/schemas/PatchedBulkOwnerRequest" } } } @@ -190046,6 +195964,22 @@ "operationId": "users_owners_retrieve", "description": "Get a owner object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -190054,6 +195988,14 @@ }, "description": "A unique integer value identifying this owner.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -190222,6 +196164,14 @@ "operationId": "users_permissions_list", "description": "Get a list of permission objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "can_add", @@ -190408,6 +196358,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -190906,6 +196864,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -191083,7 +197049,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ObjectPermissionRequest" + "$ref": "#/components/schemas/BulkObjectPermissionRequest" } } }, @@ -191091,7 +197057,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ObjectPermissionRequest" + "$ref": "#/components/schemas/BulkObjectPermissionRequest" } } } @@ -191134,7 +197100,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ObjectPermissionRequest" + "$ref": "#/components/schemas/PatchedBulkObjectPermissionRequest" } } }, @@ -191142,7 +197108,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ObjectPermissionRequest" + "$ref": "#/components/schemas/PatchedBulkObjectPermissionRequest" } } } @@ -191220,6 +197186,22 @@ "operationId": "users_permissions_retrieve", "description": "Get a permission object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -191228,6 +197210,14 @@ }, "description": "A unique integer value identifying this permission.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -191396,6 +197386,14 @@ "operationId": "users_tokens_list", "description": "Get a list of token objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -191602,6 +197600,14 @@ "format": "date-time" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -191880,6 +197886,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -192307,7 +198321,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TokenRequest" + "$ref": "#/components/schemas/BulkTokenRequest" } } }, @@ -192315,7 +198329,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TokenRequest" + "$ref": "#/components/schemas/BulkTokenRequest" } } } @@ -192358,7 +198372,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TokenRequest" + "$ref": "#/components/schemas/PatchedBulkTokenRequest" } } }, @@ -192366,7 +198380,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TokenRequest" + "$ref": "#/components/schemas/PatchedBulkTokenRequest" } } } @@ -192444,6 +198458,22 @@ "operationId": "users_tokens_retrieve", "description": "Get a token object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -192452,6 +198482,14 @@ }, "description": "A unique integer value identifying this token.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -192675,6 +198713,14 @@ "operationId": "users_users_list", "description": "Get a list of user objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "date_joined", @@ -192911,6 +198957,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "first_name", @@ -193493,6 +199547,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -193847,7 +199909,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/UserRequest" + "$ref": "#/components/schemas/BulkUserRequest" } } }, @@ -193855,7 +199917,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/UserRequest" + "$ref": "#/components/schemas/BulkUserRequest" } } } @@ -193898,7 +199960,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/UserRequest" + "$ref": "#/components/schemas/PatchedBulkUserRequest" } } }, @@ -193906,7 +199968,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/UserRequest" + "$ref": "#/components/schemas/PatchedBulkUserRequest" } } } @@ -193984,6 +200046,22 @@ "operationId": "users_users_retrieve", "description": "Get a user object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -193992,6 +200070,14 @@ }, "description": "A unique integer value identifying this user.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -194160,6 +200246,14 @@ "operationId": "virtualization_cluster_groups_list", "description": "Get a list of cluster group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -194486,6 +200580,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -194839,6 +200941,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -195277,7 +201387,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterGroupRequest" + "$ref": "#/components/schemas/BulkClusterGroupRequest" } } }, @@ -195285,7 +201395,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterGroupRequest" + "$ref": "#/components/schemas/BulkClusterGroupRequest" } } } @@ -195328,7 +201438,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterGroupRequest" + "$ref": "#/components/schemas/PatchedBulkClusterGroupRequest" } } }, @@ -195336,7 +201446,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterGroupRequest" + "$ref": "#/components/schemas/PatchedBulkClusterGroupRequest" } } } @@ -195414,6 +201524,22 @@ "operationId": "virtualization_cluster_groups_retrieve", "description": "Get a cluster group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -195422,6 +201548,14 @@ }, "description": "A unique integer value identifying this cluster group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -195590,6 +201724,14 @@ "operationId": "virtualization_cluster_types_list", "description": "Get a list of cluster type objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -195840,6 +201982,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -196193,6 +202343,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -196631,7 +202789,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterTypeRequest" + "$ref": "#/components/schemas/BulkClusterTypeRequest" } } }, @@ -196639,7 +202797,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterTypeRequest" + "$ref": "#/components/schemas/BulkClusterTypeRequest" } } } @@ -196682,7 +202840,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterTypeRequest" + "$ref": "#/components/schemas/PatchedBulkClusterTypeRequest" } } }, @@ -196690,7 +202848,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterTypeRequest" + "$ref": "#/components/schemas/PatchedBulkClusterTypeRequest" } } } @@ -196768,6 +202926,22 @@ "operationId": "virtualization_cluster_types_retrieve", "description": "Get a cluster type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -196776,6 +202950,14 @@ }, "description": "A unique integer value identifying this cluster type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -196944,6 +203126,14 @@ "operationId": "virtualization_clusters_list", "description": "Get a list of cluster objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -197270,6 +203460,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -197725,6 +203923,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -198586,7 +204792,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterRequest" + "$ref": "#/components/schemas/BulkClusterRequest" } } }, @@ -198594,7 +204800,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterRequest" + "$ref": "#/components/schemas/BulkClusterRequest" } } } @@ -198637,7 +204843,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterRequest" + "$ref": "#/components/schemas/PatchedBulkClusterRequest" } } }, @@ -198645,7 +204851,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ClusterRequest" + "$ref": "#/components/schemas/PatchedBulkClusterRequest" } } } @@ -198723,6 +204929,22 @@ "operationId": "virtualization_clusters_retrieve", "description": "Get a cluster object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -198731,6 +204953,14 @@ }, "description": "A unique integer value identifying this cluster.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -198925,6 +205155,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cluster", @@ -199234,6 +205472,14 @@ "type": "boolean" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -200064,6 +206310,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -200605,7 +206859,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VMInterfaceRequest" + "$ref": "#/components/schemas/BulkVMInterfaceRequest" } } }, @@ -200613,7 +206867,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VMInterfaceRequest" + "$ref": "#/components/schemas/BulkVMInterfaceRequest" } } } @@ -200656,7 +206910,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VMInterfaceRequest" + "$ref": "#/components/schemas/PatchedBulkVMInterfaceRequest" } } }, @@ -200664,7 +206918,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VMInterfaceRequest" + "$ref": "#/components/schemas/PatchedBulkVMInterfaceRequest" } } } @@ -200742,6 +206996,22 @@ "operationId": "virtualization_interfaces_retrieve", "description": "Get a interface object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -200750,6 +207020,14 @@ }, "description": "A unique integer value identifying this interface.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -200918,6 +207196,14 @@ "operationId": "virtualization_virtual_disks_list", "description": "Get a list of virtual disk objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -201168,6 +207454,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -201521,6 +207815,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -201945,7 +208247,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDiskRequest" + "$ref": "#/components/schemas/BulkVirtualDiskRequest" } } }, @@ -201953,7 +208255,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDiskRequest" + "$ref": "#/components/schemas/BulkVirtualDiskRequest" } } } @@ -201996,7 +208298,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDiskRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualDiskRequest" } } }, @@ -202004,7 +208306,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualDiskRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualDiskRequest" } } } @@ -202082,6 +208384,22 @@ "operationId": "virtualization_virtual_disks_retrieve", "description": "Get a virtual disk object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -202090,6 +208408,14 @@ }, "description": "A unique integer value identifying this virtual disk.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -202258,6 +208584,14 @@ "operationId": "virtualization_virtual_machine_types_list", "description": "Get a list of virtual machine type objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -202726,6 +209060,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -203079,6 +209421,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -203602,7 +209952,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineTypeRequest" + "$ref": "#/components/schemas/BulkVirtualMachineTypeRequest" } } }, @@ -203610,7 +209960,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineTypeRequest" + "$ref": "#/components/schemas/BulkVirtualMachineTypeRequest" } } } @@ -203653,7 +210003,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineTypeRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualMachineTypeRequest" } } }, @@ -203661,7 +210011,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineTypeRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualMachineTypeRequest" } } } @@ -203739,6 +210089,22 @@ "operationId": "virtualization_virtual_machine_types_retrieve", "description": "Get a virtual machine type object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -203747,6 +210113,14 @@ }, "description": "A unique integer value identifying this virtual machine type.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -203915,6 +210289,14 @@ "operationId": "virtualization_virtual_machines_list", "description": "Get a list of virtual machine objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "cluster", @@ -204568,6 +210950,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "has_primary_ip", @@ -205251,6 +211641,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -206693,7 +213091,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineWithConfigContextRequest" + "$ref": "#/components/schemas/BulkVirtualMachineWithConfigContextRequest" } } }, @@ -206701,7 +213099,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineWithConfigContextRequest" + "$ref": "#/components/schemas/BulkVirtualMachineWithConfigContextRequest" } } } @@ -206744,7 +213142,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineWithConfigContextRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualMachineWithConfigContextRequest" } } }, @@ -206752,7 +213150,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/VirtualMachineWithConfigContextRequest" + "$ref": "#/components/schemas/PatchedBulkVirtualMachineWithConfigContextRequest" } } } @@ -206830,6 +213228,22 @@ "operationId": "virtualization_virtual_machines_retrieve", "description": "Get a virtual machine object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -206838,6 +213252,14 @@ }, "description": "A unique integer value identifying this virtual machine.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -207110,6 +213532,14 @@ "operationId": "vpn_ike_policies_list", "description": "Get a list of IKE policy objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -207360,6 +213790,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -207936,6 +214374,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -208463,7 +214909,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEPolicyRequest" + "$ref": "#/components/schemas/BulkIKEPolicyRequest" } } }, @@ -208471,7 +214917,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEPolicyRequest" + "$ref": "#/components/schemas/BulkIKEPolicyRequest" } } } @@ -208514,7 +214960,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEPolicyRequest" + "$ref": "#/components/schemas/PatchedBulkIKEPolicyRequest" } } }, @@ -208522,7 +214968,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEPolicyRequest" + "$ref": "#/components/schemas/PatchedBulkIKEPolicyRequest" } } } @@ -208600,6 +215046,22 @@ "operationId": "vpn_ike_policies_retrieve", "description": "Get a IKE policy object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -208608,6 +215070,14 @@ }, "description": "A unique integer value identifying this IKE policy.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -209114,6 +215584,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -209527,6 +216005,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -210100,6 +216586,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -210472,7 +216966,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEProposalRequest" + "$ref": "#/components/schemas/BulkIKEProposalRequest" } } }, @@ -210480,7 +216974,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEProposalRequest" + "$ref": "#/components/schemas/BulkIKEProposalRequest" } } } @@ -210523,7 +217017,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEProposalRequest" + "$ref": "#/components/schemas/PatchedBulkIKEProposalRequest" } } }, @@ -210531,7 +217025,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IKEProposalRequest" + "$ref": "#/components/schemas/PatchedBulkIKEProposalRequest" } } } @@ -210609,6 +217103,22 @@ "operationId": "vpn_ike_proposals_retrieve", "description": "Get a IKE proposal object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -210617,6 +217127,14 @@ }, "description": "A unique integer value identifying this IKE proposal.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -210785,6 +217303,14 @@ "operationId": "vpn_ipsec_policies_list", "description": "Get a list of IPSec policy objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -211035,6 +217561,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -211436,6 +217970,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -211903,7 +218445,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecPolicyRequest" + "$ref": "#/components/schemas/BulkIPSecPolicyRequest" } } }, @@ -211911,7 +218453,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecPolicyRequest" + "$ref": "#/components/schemas/BulkIPSecPolicyRequest" } } } @@ -211954,7 +218496,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecPolicyRequest" + "$ref": "#/components/schemas/PatchedBulkIPSecPolicyRequest" } } }, @@ -211962,7 +218504,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecPolicyRequest" + "$ref": "#/components/schemas/PatchedBulkIPSecPolicyRequest" } } } @@ -212040,6 +218582,22 @@ "operationId": "vpn_ipsec_policies_retrieve", "description": "Get a IPSec policy object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -212048,6 +218606,14 @@ }, "description": "A unique integer value identifying this IPSec policy.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -212216,6 +218782,14 @@ "operationId": "vpn_ipsec_profiles_list", "description": "Get a list of IPSec profile objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -212466,6 +219040,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -213086,6 +219668,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -213373,7 +219963,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProfileRequest" + "$ref": "#/components/schemas/BulkIPSecProfileRequest" } } }, @@ -213381,7 +219971,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProfileRequest" + "$ref": "#/components/schemas/BulkIPSecProfileRequest" } } } @@ -213424,7 +220014,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProfileRequest" + "$ref": "#/components/schemas/PatchedBulkIPSecProfileRequest" } } }, @@ -213432,7 +220022,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProfileRequest" + "$ref": "#/components/schemas/PatchedBulkIPSecProfileRequest" } } } @@ -213510,6 +220100,22 @@ "operationId": "vpn_ipsec_profiles_retrieve", "description": "Get a IPSec profile object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -213518,6 +220124,14 @@ }, "description": "A unique integer value identifying this IPSec profile.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -213873,6 +220487,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -214310,6 +220932,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -214715,6 +221345,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -215172,7 +221810,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProposalRequest" + "$ref": "#/components/schemas/BulkIPSecProposalRequest" } } }, @@ -215180,7 +221818,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProposalRequest" + "$ref": "#/components/schemas/BulkIPSecProposalRequest" } } } @@ -215223,7 +221861,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProposalRequest" + "$ref": "#/components/schemas/PatchedBulkIPSecProposalRequest" } } }, @@ -215231,7 +221869,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/IPSecProposalRequest" + "$ref": "#/components/schemas/PatchedBulkIPSecProposalRequest" } } } @@ -215309,6 +221947,22 @@ "operationId": "vpn_ipsec_proposals_retrieve", "description": "Get a IPSec proposal object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -215317,6 +221971,14 @@ }, "description": "A unique integer value identifying this IPSec proposal.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -215618,6 +222280,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -215771,6 +222441,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -216077,6 +222755,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -216520,7 +223206,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNTerminationRequest" + "$ref": "#/components/schemas/BulkL2VPNTerminationRequest" } } }, @@ -216528,7 +223214,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNTerminationRequest" + "$ref": "#/components/schemas/BulkL2VPNTerminationRequest" } } } @@ -216571,7 +223257,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkL2VPNTerminationRequest" } } }, @@ -216579,7 +223265,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkL2VPNTerminationRequest" } } } @@ -216657,6 +223343,22 @@ "operationId": "vpn_l2vpn_terminations_retrieve", "description": "Get a L2VPN termination object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -216665,6 +223367,14 @@ }, "description": "A unique integer value identifying this L2VPN termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -216833,6 +223543,14 @@ "operationId": "vpn_l2vpns_list", "description": "Get a list of L2VPN objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -217211,6 +223929,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -217701,6 +224427,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -218567,7 +225301,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNRequest" + "$ref": "#/components/schemas/BulkL2VPNRequest" } } }, @@ -218575,7 +225309,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNRequest" + "$ref": "#/components/schemas/BulkL2VPNRequest" } } } @@ -218618,7 +225352,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNRequest" + "$ref": "#/components/schemas/PatchedBulkL2VPNRequest" } } }, @@ -218626,7 +225360,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/L2VPNRequest" + "$ref": "#/components/schemas/PatchedBulkL2VPNRequest" } } } @@ -218704,6 +225438,22 @@ "operationId": "vpn_l2vpns_retrieve", "description": "Get a L2VPN object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -218712,6 +225462,14 @@ }, "description": "A unique integer value identifying this L2VPN.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -218880,6 +225638,14 @@ "operationId": "vpn_tunnel_groups_list", "description": "Get a list of tunnel group objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -219206,6 +225972,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -219559,6 +226333,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -219997,7 +226779,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelGroupRequest" + "$ref": "#/components/schemas/BulkTunnelGroupRequest" } } }, @@ -220005,7 +226787,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelGroupRequest" + "$ref": "#/components/schemas/BulkTunnelGroupRequest" } } } @@ -220048,7 +226830,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelGroupRequest" + "$ref": "#/components/schemas/PatchedBulkTunnelGroupRequest" } } }, @@ -220056,7 +226838,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelGroupRequest" + "$ref": "#/components/schemas/PatchedBulkTunnelGroupRequest" } } } @@ -220134,6 +226916,22 @@ "operationId": "vpn_tunnel_groups_retrieve", "description": "Get a tunnel group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -220142,6 +226940,14 @@ }, "description": "A unique integer value identifying this tunnel group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -220310,6 +227116,14 @@ "operationId": "vpn_tunnel_terminations_list", "description": "Get a list of tunnel termination objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -220409,6 +227223,14 @@ "format": "uuid" } }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -220663,6 +227485,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -221246,7 +228076,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelTerminationRequest" + "$ref": "#/components/schemas/BulkTunnelTerminationRequest" } } }, @@ -221254,7 +228084,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelTerminationRequest" + "$ref": "#/components/schemas/BulkTunnelTerminationRequest" } } } @@ -221297,7 +228127,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkTunnelTerminationRequest" } } }, @@ -221305,7 +228135,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelTerminationRequest" + "$ref": "#/components/schemas/PatchedBulkTunnelTerminationRequest" } } } @@ -221383,6 +228213,22 @@ "operationId": "vpn_tunnel_terminations_retrieve", "description": "Get a tunnel termination object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -221391,6 +228237,14 @@ }, "description": "A unique integer value identifying this tunnel termination.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -221559,6 +228413,14 @@ "operationId": "vpn_tunnels_list", "description": "Get a list of tunnel objects.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "contact", @@ -222048,6 +228910,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -222509,6 +229379,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -223146,7 +230024,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelRequest" + "$ref": "#/components/schemas/BulkTunnelRequest" } } }, @@ -223154,7 +230032,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelRequest" + "$ref": "#/components/schemas/BulkTunnelRequest" } } } @@ -223197,7 +230075,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelRequest" + "$ref": "#/components/schemas/PatchedBulkTunnelRequest" } } }, @@ -223205,7 +230083,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TunnelRequest" + "$ref": "#/components/schemas/PatchedBulkTunnelRequest" } } } @@ -223283,6 +230161,22 @@ "operationId": "vpn_tunnels_retrieve", "description": "Get a tunnel object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -223291,6 +230185,14 @@ }, "description": "A unique integer value identifying this tunnel.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -223507,6 +230409,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -223757,6 +230667,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -224110,6 +231028,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -224598,7 +231524,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANGroupRequest" + "$ref": "#/components/schemas/BulkWirelessLANGroupRequest" } } }, @@ -224606,7 +231532,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANGroupRequest" + "$ref": "#/components/schemas/BulkWirelessLANGroupRequest" } } } @@ -224649,7 +231575,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANGroupRequest" + "$ref": "#/components/schemas/PatchedBulkWirelessLANGroupRequest" } } }, @@ -224657,7 +231583,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANGroupRequest" + "$ref": "#/components/schemas/PatchedBulkWirelessLANGroupRequest" } } } @@ -224735,6 +231661,22 @@ "operationId": "wireless_wireless_lan_groups_retrieve", "description": "Get a wireless LAN group object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -224743,6 +231685,14 @@ }, "description": "A unique integer value identifying this wireless LAN group.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -225436,6 +232386,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -225686,6 +232644,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "group", @@ -226008,6 +232974,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -226994,7 +233968,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANRequest" + "$ref": "#/components/schemas/BulkWirelessLANRequest" } } }, @@ -227002,7 +233976,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANRequest" + "$ref": "#/components/schemas/BulkWirelessLANRequest" } } } @@ -227045,7 +234019,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANRequest" + "$ref": "#/components/schemas/PatchedBulkWirelessLANRequest" } } }, @@ -227053,7 +234027,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLANRequest" + "$ref": "#/components/schemas/PatchedBulkWirelessLANRequest" } } } @@ -227131,6 +234105,22 @@ "operationId": "wireless_wireless_lans_retrieve", "description": "Get a wireless LAN object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -227139,6 +234129,14 @@ }, "description": "A unique integer value identifying this wireless LAN.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -227832,6 +234830,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, { "in": "query", "name": "created", @@ -228328,6 +235334,14 @@ "explode": true, "style": "form" }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "query", "name": "id", @@ -228578,6 +235592,14 @@ "type": "integer" } }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." + }, { "name": "ordering", "required": false, @@ -229281,7 +236303,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLinkRequest" + "$ref": "#/components/schemas/BulkWirelessLinkRequest" } } }, @@ -229289,7 +236311,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLinkRequest" + "$ref": "#/components/schemas/BulkWirelessLinkRequest" } } } @@ -229332,7 +236354,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLinkRequest" + "$ref": "#/components/schemas/PatchedBulkWirelessLinkRequest" } } }, @@ -229340,7 +236362,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/WirelessLinkRequest" + "$ref": "#/components/schemas/PatchedBulkWirelessLinkRequest" } } } @@ -229418,6 +236440,22 @@ "operationId": "wireless_wireless_links_retrieve", "description": "Get a wireless link object.", "parameters": [ + { + "in": "query", + "name": "brief", + "schema": { + "type": "boolean" + }, + "description": "Return only brief fields for each object." + }, + { + "in": "query", + "name": "fields", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to include in the response. Example: `fields=id,name`." + }, { "in": "path", "name": "id", @@ -229426,6 +236464,14 @@ }, "description": "A unique integer value identifying this wireless link.", "required": true + }, + { + "in": "query", + "name": "omit", + "schema": { + "type": "string" + }, + "description": "Comma-separated list of fields to exclude from the response. Example: `omit=description,tags`." } ], "tags": [ @@ -235168,6 +242214,13232 @@ "slug" ] }, + "BulkASNRangeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "rir": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefRIRRequest" + } + ] + }, + "start": { + "type": "integer", + "maximum": 4294967295, + "minimum": 1, + "format": "int64" + }, + "end": { + "type": "integer", + "maximum": 4294967295, + "minimum": 1, + "format": "int64" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "end", + "id", + "name", + "rir", + "slug", + "start" + ] + }, + "BulkASNRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "asn": { + "type": "integer", + "maximum": 4294967295, + "minimum": 1, + "format": "int64", + "description": "16- or 32-bit autonomous system number" + }, + "rir": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRIRRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": {} + }, + "sites": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "asn", + "id" + ] + }, + "BulkAggregateRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "prefix": { + "type": "string", + "minLength": 1 + }, + "rir": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefRIRRequest" + } + ] + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "date_added": { + "type": "string", + "format": "date", + "nullable": true + }, + "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": [ + "id", + "prefix", + "rir" + ] + }, + "BulkBookmarkRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + } + }, + "required": [ + "id", + "object_id", + "object_type", + "user" + ] + }, + "BulkCableBundleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name" + ] + }, + "BulkCableRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "type": { + "enum": [ + "cat3", + "cat5", + "cat5e", + "cat6", + "cat6a", + "cat7", + "cat7a", + "cat8", + "mrj21-trunk", + "dac-active", + "dac-passive", + "coaxial", + "rg-6", + "rg-8", + "rg-11", + "rg-59", + "rg-62", + "rg-213", + "lmr-100", + "lmr-200", + "lmr-400", + "mmf", + "mmf-om1", + "mmf-om2", + "mmf-om3", + "mmf-om4", + "mmf-om5", + "smf", + "smf-os1", + "smf-os2", + "aoc", + "power", + "usb", + "", + null + ], + "type": "string", + "description": "* `cat3` - CAT3\n* `cat5` - CAT5\n* `cat5e` - CAT5e\n* `cat6` - CAT6\n* `cat6a` - CAT6a\n* `cat7` - CAT7\n* `cat7a` - CAT7a\n* `cat8` - CAT8\n* `mrj21-trunk` - MRJ21 Trunk\n* `dac-active` - Direct Attach Copper (Active)\n* `dac-passive` - Direct Attach Copper (Passive)\n* `coaxial` - Coaxial\n* `rg-6` - RG-6\n* `rg-8` - RG-8\n* `rg-11` - RG-11\n* `rg-59` - RG-59\n* `rg-62` - RG-62\n* `rg-213` - RG-213\n* `lmr-100` - LMR-100\n* `lmr-200` - LMR-200\n* `lmr-400` - LMR-400\n* `mmf` - Multimode Fiber\n* `mmf-om1` - Multimode Fiber (OM1)\n* `mmf-om2` - Multimode Fiber (OM2)\n* `mmf-om3` - Multimode Fiber (OM3)\n* `mmf-om4` - Multimode Fiber (OM4)\n* `mmf-om5` - Multimode Fiber (OM5)\n* `smf` - Single-mode Fiber\n* `smf-os1` - Single-mode Fiber (OS1)\n* `smf-os2` - Single-mode Fiber (OS2)\n* `aoc` - Active Optical Cabling (AOC)\n* `power` - Power\n* `usb` - USB", + "x-spec-enum-id": "3d4d8d7ae24f7be8", + "nullable": true + }, + "a_terminations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GenericObjectRequest" + } + }, + "b_terminations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GenericObjectRequest" + } + }, + "status": { + "enum": [ + "connected", + "planned", + "decommissioning" + ], + "type": "string", + "description": "* `connected` - Connected\n* `planned` - Planned\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "80d251a40f3a3144" + }, + "profile": { + "enum": [ + "single-1c1p", + "single-1c2p", + "single-1c4p", + "single-1c6p", + "single-1c8p", + "single-1c12p", + "single-1c16p", + "trunk-2c1p", + "trunk-2c2p", + "trunk-2c4p", + "trunk-2c4p-shuffle", + "trunk-2c6p", + "trunk-2c8p", + "trunk-2c12p", + "trunk-4c1p", + "trunk-4c2p", + "trunk-4c4p", + "trunk-4c4p-shuffle", + "trunk-4c6p", + "trunk-4c8p", + "trunk-8c4p", + "breakout-1c2p-2c1p", + "breakout-1c4p-4c1p", + "breakout-1c6p-6c1p", + "breakout-2c4p-8c1p-shuffle" + ], + "type": "string", + "description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c2p-2c1p` - 1C2P:2C1P breakout\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)", + "x-spec-enum-id": "f566e6df6572f5d0" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "bundle": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCableBundleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "label": { + "type": "string", + "maxLength": 100 + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "length": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "length_unit": { + "enum": [ + "km", + "m", + "cm", + "mi", + "ft", + "in", + "", + null + ], + "type": "string", + "description": "* `km` - Kilometers\n* `m` - Meters\n* `cm` - Centimeters\n* `mi` - Miles\n* `ft` - Feet\n* `in` - Inches", + "x-spec-enum-id": "6e7645525ba02462", + "nullable": true + }, + "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": [ + "id" + ] + }, + "BulkCircuitGroupAssignmentRequest": { + "type": "object", + "description": "Base serializer for group assignments under CircuitSerializer.", + "properties": { + "id": { + "type": "integer" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefCircuitGroupRequest" + } + ] + }, + "member_type": { + "type": "string" + }, + "member_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "priority": { + "enum": [ + "primary", + "secondary", + "tertiary", + "inactive", + "" + ], + "type": "string", + "description": "* `primary` - Primary\n* `secondary` - Secondary\n* `tertiary` - Tertiary\n* `inactive` - Inactive", + "x-spec-enum-id": "0548fc537440bf9d" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "group", + "id", + "member_id", + "member_type" + ] + }, + "BulkCircuitGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkCircuitRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "cid": { + "type": "string", + "minLength": 1, + "title": "Circuit ID", + "description": "Unique circuit ID", + "maxLength": 100 + }, + "provider": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderRequest" + } + ] + }, + "provider_account": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefProviderAccountRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefCircuitTypeRequest" + } + ] + }, + "status": { + "enum": [ + "planned", + "provisioning", + "active", + "offline", + "deprovisioning", + "decommissioned" + ], + "type": "string", + "description": "* `planned` - Planned\n* `provisioning` - Provisioning\n* `active` - Active\n* `offline` - Offline\n* `deprovisioning` - Deprovisioning\n* `decommissioned` - Decommissioned", + "x-spec-enum-id": "0a239d878b6666a4" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "install_date": { + "type": "string", + "format": "date", + "nullable": true, + "title": "Installed" + }, + "termination_date": { + "type": "string", + "format": "date", + "nullable": true, + "title": "Terminates" + }, + "commit_rate": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Commit rate (Kbps)", + "description": "Committed rate" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "distance": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "distance_unit": { + "enum": [ + "km", + "m", + "mi", + "ft", + "", + null + ], + "type": "string", + "description": "* `km` - Kilometers\n* `m` - Meters\n* `mi` - Miles\n* `ft` - Feet", + "x-spec-enum-id": "b1169a409430c02e", + "nullable": true + }, + "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": {} + }, + "assignments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BriefCircuitGroupAssignmentSerializer_Request" + } + } + }, + "required": [ + "cid", + "id", + "provider", + "type" + ] + }, + "BulkCircuitTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "circuit": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefCircuitRequest" + } + ] + }, + "term_side": { + "enum": [ + "A", + "Z" + ], + "type": "string", + "description": "* `A` - A\n* `Z` - Z", + "x-spec-enum-id": "95b8fcc737f355d0", + "title": "Termination side" + }, + "termination_type": { + "type": "string", + "nullable": true + }, + "termination_id": { + "type": "integer", + "nullable": true + }, + "port_speed": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Port speed (Kbps)", + "description": "Physical circuit speed" + }, + "upstream_speed": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Upstream speed (Kbps)", + "description": "Upstream speed, if different from port speed" + }, + "xconnect_id": { + "type": "string", + "title": "Cross-connect ID", + "description": "ID of the local cross-connect", + "maxLength": 50 + }, + "pp_info": { + "type": "string", + "title": "Patch panel/port(s)", + "description": "Patch panel ID and port number(s)", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "circuit", + "id", + "term_side" + ] + }, + "BulkCircuitTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkClusterGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkClusterRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefClusterTypeRequest" + } + ] + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefClusterGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "planned", + "staging", + "active", + "decommissioning", + "offline" + ], + "type": "string", + "description": "* `planned` - Planned\n* `staging` - Staging\n* `active` - Active\n* `decommissioning` - Decommissioning\n* `offline` - Offline", + "x-spec-enum-id": "65a25166053759eb" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "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": [ + "id", + "name", + "type" + ] + }, + "BulkClusterTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkConfigContextProfileRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "schema": { + "nullable": true, + "description": "A JSON schema specifying the structure of the context data for this profile" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "data_source": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkConfigContextRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "profile": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigContextProfileRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "is_active": { + "type": "boolean" + }, + "regions": { + "type": "array", + "items": { + "type": "integer" + } + }, + "site_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "sites": { + "type": "array", + "items": { + "type": "integer" + } + }, + "locations": { + "type": "array", + "items": { + "type": "integer" + } + }, + "device_types": { + "type": "array", + "items": { + "type": "integer" + } + }, + "roles": { + "type": "array", + "items": { + "type": "integer" + } + }, + "platforms": { + "type": "array", + "items": { + "type": "integer" + } + }, + "cluster_types": { + "type": "array", + "items": { + "type": "integer" + } + }, + "cluster_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "clusters": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tenant_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tenants": { + "type": "array", + "items": { + "type": "integer" + } + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "data_source": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + }, + "data": {} + }, + "required": [ + "data", + "id", + "name" + ] + }, + "BulkConfigTemplateRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "environment_params": { + "nullable": true, + "title": "Environment parameters", + "description": "Any additional parameters to pass when constructing the Jinja environment" + }, + "template_code": { + "type": "string", + "minLength": 1, + "description": "Jinja template code." + }, + "mime_type": { + "type": "string", + "description": "Defaults to text/plain; charset=utf-8", + "maxLength": 50 + }, + "file_name": { + "type": "string", + "description": "Filename to give to the rendered export file", + "maxLength": 200 + }, + "file_extension": { + "type": "string", + "description": "Extension to append to the rendered filename", + "maxLength": 15 + }, + "as_attachment": { + "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": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + }, + "auto_sync_enabled": { + "type": "boolean", + "description": "Enable automatic synchronization of data when the data file is updated" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "id", + "name", + "template_code" + ] + }, + "BulkConsolePortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "speed": { + "enum": [ + 1200, + 2400, + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + null + ], + "type": "integer", + "description": "* `1200` - 1200 bps\n* `2400` - 2400 bps\n* `4800` - 4800 bps\n* `9600` - 9600 bps\n* `19200` - 19.2 kbps\n* `38400` - 38.4 kbps\n* `57600` - 57.6 kbps\n* `115200` - 115.2 kbps", + "x-spec-enum-id": "ab6d9635c131a378", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkConsolePortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkConsoleServerPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "speed": { + "enum": [ + 1200, + 2400, + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + null + ], + "type": "integer", + "description": "* `1200` - 1200 bps\n* `2400` - 2400 bps\n* `4800` - 4800 bps\n* `9600` - 9600 bps\n* `19200` - 19.2 kbps\n* `38400` - 38.4 kbps\n* `57600` - 57.6 kbps\n* `115200` - 115.2 kbps", + "x-spec-enum-id": "ab6d9635c131a378", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkConsoleServerPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkContactAssignmentRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "contact": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefContactRequest" + } + ] + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefContactRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "priority": { + "enum": [ + "primary", + "secondary", + "tertiary", + "inactive", + "" + ], + "type": "string", + "description": "* `primary` - Primary\n* `secondary` - Secondary\n* `tertiary` - Tertiary\n* `inactive` - Inactive", + "x-spec-enum-id": "0548fc537440bf9d" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "contact", + "id", + "object_id", + "object_type" + ] + }, + "BulkContactGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedContactGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkContactRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "title": { + "type": "string", + "maxLength": 100 + }, + "phone": { + "type": "string", + "maxLength": 50 + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 254 + }, + "address": { + "type": "string", + "maxLength": 200 + }, + "link": { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + "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": [ + "id", + "name" + ] + }, + "BulkContactRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkCustomFieldChoiceSetRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "base_choices": { + "enum": [ + "IATA", + "ISO_3166", + "UN_LOCODE" + ], + "type": "string", + "description": "* `IATA` - IATA (Airport codes)\n* `ISO_3166` - ISO 3166 (Country codes)\n* `UN_LOCODE` - UN/LOCODE (Location codes)", + "x-spec-enum-id": "cf0efb5195f85007" + }, + "extra_choices": { + "type": "array", + "items": { + "type": "array", + "items": {}, + "maxItems": 2, + "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" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "extra_choices", + "id", + "name" + ] + }, + "BulkCustomFieldRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "enum": [ + "text", + "longtext", + "integer", + "decimal", + "boolean", + "date", + "datetime", + "url", + "json", + "select", + "multiselect", + "object", + "multiobject" + ], + "type": "string", + "description": "* `text` - Text\n* `longtext` - Text (long)\n* `integer` - Integer\n* `decimal` - Decimal\n* `boolean` - Boolean (true/false)\n* `date` - Date\n* `datetime` - Date & time\n* `url` - URL\n* `json` - JSON\n* `select` - Selection\n* `multiselect` - Multiple selection\n* `object` - Object\n* `multiobject` - Multiple objects", + "x-spec-enum-id": "47c52a3d983e924c" + }, + "related_object_type": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Internal field name", + "pattern": "^[a-z0-9_]+$", + "maxLength": 50 + }, + "label": { + "type": "string", + "description": "Name of the field as displayed to users (if not provided, 'the field's name will be used)", + "maxLength": 50 + }, + "group_name": { + "type": "string", + "description": "Custom fields within the same group will be displayed together", + "maxLength": 50 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "required": { + "type": "boolean", + "description": "This field is required when creating new objects or editing an existing object." + }, + "unique": { + "type": "boolean", + "title": "Must be unique", + "description": "The value of this field must be unique for the assigned object" + }, + "search_weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "description": "Weighting for search. Lower values are considered more important. Fields with a search weight of zero will be ignored." + }, + "filter_logic": { + "enum": [ + "disabled", + "loose", + "exact" + ], + "type": "string", + "description": "* `disabled` - Disabled\n* `loose` - Loose\n* `exact` - Exact", + "x-spec-enum-id": "d168820c798ae45a" + }, + "ui_visible": { + "enum": [ + "always", + "if-set", + "hidden" + ], + "type": "string", + "description": "* `always` - Always\n* `if-set` - If set\n* `hidden` - Hidden", + "x-spec-enum-id": "f32800c399b927b6" + }, + "ui_editable": { + "enum": [ + "yes", + "no", + "hidden" + ], + "type": "string", + "description": "* `yes` - Yes\n* `no` - No\n* `hidden` - Hidden", + "x-spec-enum-id": "336f52760e62022f" + }, + "is_cloneable": { + "type": "boolean", + "description": "Replicate this value when cloning objects" + }, + "default": { + "nullable": true, + "description": "Default value for the field (must be a JSON value). Encapsulate strings with double quotes (e.g. \"Foo\")." + }, + "related_object_filter": { + "nullable": true, + "description": "Filter the object selection choices using a query_params dict (must be a JSON value).Encapsulate strings with double quotes (e.g. \"Foo\")." + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "title": "Display weight", + "description": "Fields with higher weights appear lower in a form." + }, + "validation_minimum": { + "type": "number", + "format": "double", + "maximum": 1000000000000, + "minimum": -1000000000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Minimum value", + "description": "Minimum allowed value (for numeric fields)" + }, + "validation_maximum": { + "type": "number", + "format": "double", + "maximum": 1000000000000, + "minimum": -1000000000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Maximum value", + "description": "Maximum allowed value (for numeric fields)" + }, + "validation_regex": { + "type": "string", + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCustomFieldChoiceSetRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "object_types", + "type" + ] + }, + "BulkCustomLinkRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "enabled": { + "type": "boolean" + }, + "link_text": { + "type": "string", + "minLength": 1, + "description": "Jinja2 template code for link text" + }, + "link_url": { + "type": "string", + "minLength": 1, + "description": "Jinja2 template code for link URL" + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "group_name": { + "type": "string", + "description": "Links with the same group will appear as a dropdown menu", + "maxLength": 50 + }, + "button_class": { + "enum": [ + "default", + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "gray", + "black", + "white", + "ghost-dark" + ], + "type": "string", + "x-spec-enum-id": "5e54b3bd086685ce", + "description": "The class of the first link in a group will be used for the dropdown button\n\n* `default` - Default\n* `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\n* `ghost-dark` - Link" + }, + "new_window": { + "type": "boolean", + "description": "Force link to open in a new window" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id", + "link_text", + "link_url", + "name", + "object_types" + ] + }, + "BulkDataSourceRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "type": { + "enum": [ + null, + "local", + "git", + "amazon-s3" + ], + "description": "* `None` - ---------\n* `local` - Local\n* `git` - Git\n* `amazon-s3` - Amazon S3", + "x-spec-enum-id": "562b613a749b34b0" + }, + "source_url": { + "type": "string", + "minLength": 1, + "title": "URL", + "maxLength": 200 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "sync_interval": { + "enum": [ + 1, + 60, + 720, + 1440, + 10080, + 43200, + null + ], + "type": "integer", + "description": "* `1` - Minutely\n* `60` - Hourly\n* `720` - 12 hours\n* `1440` - Daily\n* `10080` - Weekly\n* `43200` - 30 days", + "x-spec-enum-id": "2e9f2567ecd93fbe", + "nullable": true, + "minimum": 0, + "maximum": 32767 + }, + "parameters": { + "nullable": true + }, + "ignore_rules": { + "type": "string", + "description": "Patterns (one per line) matching files or paths to ignore when syncing" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name", + "source_url", + "type" + ] + }, + "BulkDeviceBayRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "installed_device": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkDeviceBayTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "device_type", + "id", + "name" + ] + }, + "BulkDeviceRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "vm_role": { + "type": "boolean", + "description": "Virtual machines may be assigned to this role" + }, + "config_template": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedDeviceRoleRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkDeviceTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ] + }, + "default_platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "model": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "part_number": { + "type": "string", + "description": "Discrete part number (optional)", + "maxLength": 50 + }, + "u_height": { + "type": "number", + "format": "double", + "maximum": 1000, + "minimum": 0.0, + "exclusiveMaximum": true, + "default": 1.0, + "title": "Position (U)" + }, + "exclude_from_utilization": { + "type": "boolean", + "description": "Devices of this type are excluded when calculating rack utilization." + }, + "is_full_depth": { + "type": "boolean", + "description": "Device consumes both front and rear rack faces." + }, + "subdevice_role": { + "enum": [ + "parent", + "child", + "", + null + ], + "type": "string", + "description": "* `parent` - Parent\n* `child` - Child", + "x-spec-enum-id": "65a61d5e1deb4a24", + "nullable": true + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "rear-to-side", + "bottom-to-top", + "top-to-bottom", + "passive", + "mixed", + "", + null + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front\n* `left-to-right` - Left to right\n* `right-to-left` - Right to left\n* `side-to-rear` - Side to rear\n* `rear-to-side` - Rear to side\n* `bottom-to-top` - Bottom to top\n* `top-to-bottom` - Top to bottom\n* `passive` - Passive\n* `mixed` - Mixed", + "x-spec-enum-id": "11cb3d363b41ba9e", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "front_image": { + "type": "string", + "format": "binary", + "nullable": true + }, + "rear_image": { + "type": "string", + "format": "binary", + "nullable": true + }, + "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": [ + "id", + "manufacturer", + "model", + "slug" + ] + }, + "BulkDeviceWithConfigContextRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "nullable": true, + "maxLength": 64 + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ] + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRoleRequest" + } + ] + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "serial": { + "type": "string", + "title": "Serial number", + "description": "Chassis serial number, assigned by the manufacturer", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this device", + "maxLength": 50 + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "location": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefLocationRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "rack": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "position": { + "type": "number", + "format": "double", + "maximum": 1000, + "minimum": 0.5, + "exclusiveMaximum": true, + "nullable": true, + "title": "Position (U)" + }, + "face": { + "enum": [ + "front", + "rear", + "" + ], + "type": "string", + "description": "* `front` - Front\n* `rear` - Rear", + "x-spec-enum-id": "d2fb9b3f75158b83" + }, + "latitude": { + "type": "number", + "format": "double", + "maximum": 90.0, + "minimum": -90.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "longitude": { + "type": "number", + "format": "double", + "maximum": 180.0, + "minimum": -180.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "staged", + "failed", + "inventory", + "decommissioning" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `inventory` - Inventory\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "65feb4244cc9110c" + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "rear-to-side", + "bottom-to-top", + "top-to-bottom", + "passive", + "mixed", + "" + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front\n* `left-to-right` - Left to right\n* `right-to-left` - Right to left\n* `side-to-rear` - Side to rear\n* `rear-to-side` - Rear to side\n* `bottom-to-top` - Bottom to top\n* `top-to-bottom` - Top to bottom\n* `passive` - Passive\n* `mixed` - Mixed", + "x-spec-enum-id": "11cb3d363b41ba9e" + }, + "primary_ip4": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip6": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "oob_ip": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "cluster": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefClusterRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "virtual_chassis": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVirtualChassisRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vc_position": { + "type": "integer", + "maximum": 255, + "minimum": 0, + "nullable": true + }, + "vc_priority": { + "type": "integer", + "maximum": 255, + "minimum": 0, + "nullable": true, + "description": "Virtual chassis master election priority" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "config_template": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "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": {} + } + }, + "required": [ + "device_type", + "id", + "role", + "site" + ] + }, + "BulkEventRuleRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 150 + }, + "enabled": { + "type": "boolean" + }, + "event_types": { + "type": "array", + "items": { + "enum": [ + "object_created", + "object_updated", + "object_deleted", + "job_started", + "job_completed", + "job_failed", + "job_errored" + ], + "type": "string", + "description": "* `object_created` - Object created\n* `object_updated` - Object updated\n* `object_deleted` - Object deleted\n* `job_started` - Job started\n* `job_completed` - Job completed\n* `job_failed` - Job failed\n* `job_errored` - Job errored", + "x-spec-enum-id": "01e557313a5c7bd2" + }, + "description": "The types of event which will trigger this rule." + }, + "conditions": { + "nullable": true, + "description": "A set of conditions which determine whether the event will be generated." + }, + "action_type": { + "enum": [ + "webhook", + "script", + "notification" + ], + "type": "string", + "description": "* `webhook` - Webhook\n* `script` - Script\n* `notification` - Notification", + "x-spec-enum-id": "287901b937995956" + }, + "action_object_type": { + "type": "string" + }, + "action_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "action_object_type", + "action_type", + "event_types", + "id", + "name", + "object_types" + ] + }, + "BulkExportTemplateRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "environment_params": { + "nullable": true, + "title": "Environment parameters", + "description": "Any additional parameters to pass when constructing the Jinja environment" + }, + "template_code": { + "type": "string", + "minLength": 1, + "description": "Jinja template code." + }, + "mime_type": { + "type": "string", + "description": "Defaults to text/plain; charset=utf-8", + "maxLength": 50 + }, + "file_name": { + "type": "string", + "description": "Filename to give to the rendered export file", + "maxLength": 200 + }, + "file_extension": { + "type": "string", + "description": "Extension to append to the rendered filename", + "maxLength": 15 + }, + "as_attachment": { + "type": "boolean", + "description": "Download file as attachment" + }, + "data_source": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id", + "name", + "object_types", + "template_code" + ] + }, + "BulkFHRPGroupAssignmentRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefFHRPGroupRequest" + } + ] + }, + "interface_type": { + "type": "string" + }, + "interface_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "priority": { + "type": "integer", + "maximum": 255, + "minimum": 0 + } + }, + "required": [ + "group", + "id", + "interface_id", + "interface_type", + "priority" + ] + }, + "BulkFHRPGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "protocol": { + "enum": [ + "vrrp2", + "vrrp3", + "carp", + "clusterxl", + "hsrp", + "glbp", + "other" + ], + "type": "string", + "description": "* `vrrp2` - VRRPv2\n* `vrrp3` - VRRPv3\n* `carp` - CARP\n* `clusterxl` - ClusterXL\n* `hsrp` - HSRP\n* `glbp` - GLBP\n* `other` - Other", + "x-spec-enum-id": "98de93c9f65d1c65" + }, + "group_id": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "auth_type": { + "enum": [ + "plaintext", + "md5", + "", + null + ], + "type": "string", + "description": "* `plaintext` - Plaintext\n* `md5` - MD5", + "x-spec-enum-id": "565396e386e1542a", + "nullable": true, + "title": "Authentication type" + }, + "auth_key": { + "type": "string", + "title": "Authentication key", + "maxLength": 255 + }, + "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": [ + "group_id", + "id", + "protocol" + ] + }, + "BulkFrontPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "rear_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FrontPortMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name", + "type" + ] + }, + "BulkFrontPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "rear_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FrontPortTemplateMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name", + "type" + ] + }, + "BulkGroupRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 150 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "permissions": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkIKEPolicyRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "version": { + "enum": [ + 1, + 2 + ], + "type": "integer", + "description": "* `1` - IKEv1\n* `2` - IKEv2", + "x-spec-enum-id": "00872b77916a1fde" + }, + "mode": { + "enum": [ + "aggressive", + "main" + ], + "type": "string", + "description": "* `aggressive` - Aggressive\n* `main` - Main", + "x-spec-enum-id": "64c1be7bdb2548ca" + }, + "proposals": { + "type": "array", + "items": { + "type": "integer" + } + }, + "preshared_key": { + "type": "string", + "title": "Pre-shared key" + }, + "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": [ + "id", + "name", + "version" + ] + }, + "BulkIKEProposalRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "authentication_method": { + "enum": [ + "preshared-keys", + "certificates", + "rsa-signatures", + "dsa-signatures" + ], + "type": "string", + "description": "* `preshared-keys` - Pre-shared keys\n* `certificates` - Certificates\n* `rsa-signatures` - RSA signatures\n* `dsa-signatures` - DSA signatures", + "x-spec-enum-id": "a21158c52d0c455a" + }, + "encryption_algorithm": { + "enum": [ + "aes-128-cbc", + "aes-128-gcm", + "aes-192-cbc", + "aes-192-gcm", + "aes-256-cbc", + "aes-256-gcm", + "3des-cbc", + "des-cbc" + ], + "type": "string", + "description": "* `aes-128-cbc` - 128-bit AES (CBC)\n* `aes-128-gcm` - 128-bit AES (GCM)\n* `aes-192-cbc` - 192-bit AES (CBC)\n* `aes-192-gcm` - 192-bit AES (GCM)\n* `aes-256-cbc` - 256-bit AES (CBC)\n* `aes-256-gcm` - 256-bit AES (GCM)\n* `3des-cbc` - 3DES\n* `des-cbc` - DES", + "x-spec-enum-id": "ae3dabd7b2b3cba2" + }, + "authentication_algorithm": { + "enum": [ + "hmac-sha1", + "hmac-sha256", + "hmac-sha384", + "hmac-sha512", + "hmac-md5" + ], + "type": "string", + "description": "* `hmac-sha1` - SHA-1 HMAC\n* `hmac-sha256` - SHA-256 HMAC\n* `hmac-sha384` - SHA-384 HMAC\n* `hmac-sha512` - SHA-512 HMAC\n* `hmac-md5` - MD5 HMAC", + "x-spec-enum-id": "0a7ca69695b483a7" + }, + "group": { + "enum": [ + 1, + 2, + 5, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34 + ], + "type": "integer", + "description": "* `1` - Group 1\n* `2` - Group 2\n* `5` - Group 5\n* `14` - Group 14\n* `15` - Group 15\n* `16` - Group 16\n* `17` - Group 17\n* `18` - Group 18\n* `19` - Group 19\n* `20` - Group 20\n* `21` - Group 21\n* `22` - Group 22\n* `23` - Group 23\n* `24` - Group 24\n* `25` - Group 25\n* `26` - Group 26\n* `27` - Group 27\n* `28` - Group 28\n* `29` - Group 29\n* `30` - Group 30\n* `31` - Group 31\n* `32` - Group 32\n* `33` - Group 33\n* `34` - Group 34", + "x-spec-enum-id": "dbef43be795462a8" + }, + "sa_lifetime": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "description": "Security association lifetime (in seconds)" + }, + "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": [ + "authentication_method", + "encryption_algorithm", + "group", + "id", + "name" + ] + }, + "BulkIPAddressRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "address": { + "type": "string", + "minLength": 1 + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "deprecated", + "dhcp", + "slaac" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated\n* `dhcp` - DHCP\n* `slaac` - SLAAC", + "x-spec-enum-id": "c421c4c4a0fa7a2a" + }, + "role": { + "enum": [ + "loopback", + "secondary", + "anycast", + "vip", + "vrrp", + "hsrp", + "glbp", + "carp", + "" + ], + "type": "string", + "description": "* `loopback` - Loopback\n* `secondary` - Secondary\n* `anycast` - Anycast\n* `vip` - VIP\n* `vrrp` - VRRP\n* `hsrp` - HSRP\n* `glbp` - GLBP\n* `carp` - CARP", + "x-spec-enum-id": "53dca4cddd7b344a" + }, + "assigned_object_type": { + "type": "string", + "nullable": true + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "nat_inside": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedIPAddressRequest" + } + ], + "nullable": true + }, + "dns_name": { + "type": "string", + "description": "Hostname or FQDN (not case-sensitive)", + "pattern": "^([0-9A-Za-z_-]+|\\*)(\\.[0-9A-Za-z_-]+)*\\.?$", + "maxLength": 255 + }, + "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": [ + "address", + "id" + ] + }, + "BulkIPRangeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "start_address": { + "type": "string", + "minLength": 1 + }, + "end_address": { + "type": "string", + "minLength": 1 + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "deprecated" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated", + "x-spec-enum-id": "ca933c38b935e547" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": {} + }, + "mark_populated": { + "type": "boolean", + "description": "Prevent the creation of IP addresses within this range" + }, + "mark_utilized": { + "type": "boolean", + "description": "Report space as fully utilized" + } + }, + "required": [ + "end_address", + "id", + "start_address" + ] + }, + "BulkIPSecPolicyRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "proposals": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pfs_group": { + "enum": [ + 1, + 2, + 5, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34 + ], + "type": "integer", + "description": "* `1` - Group 1\n* `2` - Group 2\n* `5` - Group 5\n* `14` - Group 14\n* `15` - Group 15\n* `16` - Group 16\n* `17` - Group 17\n* `18` - Group 18\n* `19` - Group 19\n* `20` - Group 20\n* `21` - Group 21\n* `22` - Group 22\n* `23` - Group 23\n* `24` - Group 24\n* `25` - Group 25\n* `26` - Group 26\n* `27` - Group 27\n* `28` - Group 28\n* `29` - Group 29\n* `30` - Group 30\n* `31` - Group 31\n* `32` - Group 32\n* `33` - Group 33\n* `34` - Group 34", + "x-spec-enum-id": "dbef43be795462a8" + }, + "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": [ + "id", + "name" + ] + }, + "BulkIPSecProfileRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mode": { + "enum": [ + "esp", + "ah" + ], + "type": "string", + "description": "* `esp` - ESP\n* `ah` - AH", + "x-spec-enum-id": "87ac6ada0da14ccf" + }, + "ike_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefIKEPolicyRequest" + } + ] + }, + "ipsec_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefIPSecPolicyRequest" + } + ] + }, + "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": [ + "id", + "ike_policy", + "ipsec_policy", + "mode", + "name" + ] + }, + "BulkIPSecProposalRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "encryption_algorithm": { + "enum": [ + "aes-128-cbc", + "aes-128-gcm", + "aes-192-cbc", + "aes-192-gcm", + "aes-256-cbc", + "aes-256-gcm", + "3des-cbc", + "des-cbc" + ], + "type": "string", + "description": "* `aes-128-cbc` - 128-bit AES (CBC)\n* `aes-128-gcm` - 128-bit AES (GCM)\n* `aes-192-cbc` - 192-bit AES (CBC)\n* `aes-192-gcm` - 192-bit AES (GCM)\n* `aes-256-cbc` - 256-bit AES (CBC)\n* `aes-256-gcm` - 256-bit AES (GCM)\n* `3des-cbc` - 3DES\n* `des-cbc` - DES", + "x-spec-enum-id": "ae3dabd7b2b3cba2" + }, + "authentication_algorithm": { + "enum": [ + "hmac-sha1", + "hmac-sha256", + "hmac-sha384", + "hmac-sha512", + "hmac-md5" + ], + "type": "string", + "description": "* `hmac-sha1` - SHA-1 HMAC\n* `hmac-sha256` - SHA-256 HMAC\n* `hmac-sha384` - SHA-384 HMAC\n* `hmac-sha512` - SHA-512 HMAC\n* `hmac-md5` - MD5 HMAC", + "x-spec-enum-id": "0a7ca69695b483a7" + }, + "sa_lifetime_seconds": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "SA lifetime (seconds)", + "description": "Security association lifetime (seconds)" + }, + "sa_lifetime_data": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "SA lifetime (KB)", + "description": "Security association lifetime (in kilobytes)" + }, + "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": [ + "id", + "name" + ] + }, + "BulkImageAttachmentRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "name": { + "type": "string", + "maxLength": 50 + }, + "image": { + "type": "string", + "format": "binary" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "image", + "object_id", + "object_type" + ] + }, + "BulkInterfaceRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "vdcs": { + "type": "array", + "items": { + "type": "integer" + } + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "virtual", + "bridge", + "lag", + "100base-fx", + "100base-lfx", + "100base-tx", + "100base-t1", + "1000base-bx10-d", + "1000base-bx10-u", + "1000base-cwdm", + "1000base-cx", + "1000base-dwdm", + "1000base-ex", + "1000base-lsx", + "1000base-lx", + "1000base-lx10", + "1000base-sx", + "1000base-t", + "1000base-tx", + "1000base-zx", + "2.5gbase-t", + "5gbase-t", + "10gbase-br-d", + "10gbase-br-u", + "10gbase-cu", + "10gbase-cx4", + "10gbase-er", + "10gbase-lr", + "10gbase-lrm", + "10gbase-lx4", + "10gbase-sr", + "10gbase-t", + "10gbase-zr", + "25gbase-cr", + "25gbase-er", + "25gbase-lr", + "25gbase-sr", + "25gbase-t", + "40gbase-cr4", + "40gbase-er4", + "40gbase-fr4", + "40gbase-lr4", + "40gbase-sr4", + "40gbase-sr4-bd", + "50gbase-cr", + "50gbase-er", + "50gbase-fr", + "50gbase-lr", + "50gbase-sr", + "100gbase-cr1", + "100gbase-cr2", + "100gbase-cr4", + "100gbase-cr10", + "100gbase-cwdm4", + "100gbase-dr", + "100gbase-er4", + "100gbase-fr1", + "100gbase-lr1", + "100gbase-lr4", + "100gbase-sr1", + "100gbase-sr1.2", + "100gbase-sr2", + "100gbase-sr4", + "100gbase-sr10", + "100gbase-zr", + "200gbase-cr2", + "200gbase-cr4", + "200gbase-dr4", + "200gbase-er4", + "200gbase-fr4", + "200gbase-lr4", + "200gbase-sr2", + "200gbase-sr4", + "200gbase-vr2", + "400gbase-cr4", + "400gbase-dr4", + "400gbase-er8", + "400gbase-fr4", + "400gbase-fr8", + "400gbase-lr4", + "400gbase-lr8", + "400gbase-sr4", + "400gbase-sr4_2", + "400gbase-sr8", + "400gbase-sr16", + "400gbase-vr4", + "400gbase-zr", + "800gbase-cr8", + "800gbase-dr8", + "800gbase-sr8", + "800gbase-vr8", + "1.6tbase-cr8", + "1.6tbase-dr8", + "1.6tbase-dr8-2", + "100base-x-sfp", + "1000base-x-gbic", + "1000base-x-sfp", + "2.5gbase-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xenpak", + "10gbase-x-xfp", + "10gbase-x-x2", + "25gbase-x-sfp28", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "50gbase-x-sfp56", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cxp", + "100gbase-x-cpak", + "100gbase-x-dsfp", + "100gbase-x-qsfp28", + "100gbase-x-qsfpdd", + "100gbase-x-sfpdd", + "200gbase-x-cfp2", + "200gbase-x-qsfp56", + "200gbase-x-qsfpdd", + "400gbase-x-qsfp112", + "400gbase-x-qsfpdd", + "400gbase-x-cdfp", + "400gbase-x-cfp2", + "400gbase-x-cfp8", + "400gbase-x-osfp", + "400gbase-x-osfp-rhs", + "800gbase-x-osfp", + "800gbase-x-qsfpdd", + "1.6tbase-x-osfp1600", + "1.6tbase-x-osfp1600-rhs", + "1.6tbase-x-qsfpdd1600", + "1000base-kx", + "2.5gbase-kx", + "5gbase-kr", + "10gbase-kr", + "10gbase-kx4", + "25gbase-kr", + "40gbase-kr4", + "50gbase-kr", + "100gbase-kp4", + "100gbase-kr2", + "100gbase-kr4", + "1.6tbase-kr8", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "ieee802.11ax", + "ieee802.11ay", + "ieee802.11be", + "ieee802.15.1", + "ieee802.15.4", + "other-wireless", + "gsm", + "cdma", + "lte", + "4g", + "5g", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "32gfc-sfpp", + "64gfc-qsfpp", + "64gfc-sfpdd", + "64gfc-sfpp", + "128gfc-qsfp28", + "infiniband-sdr", + "infiniband-ddr", + "infiniband-qdr", + "infiniband-fdr10", + "infiniband-fdr", + "infiniband-edr", + "infiniband-hdr", + "infiniband-ndr", + "infiniband-xdr", + "t1", + "e1", + "t3", + "e3", + "xdsl", + "docsis", + "moca", + "bpon", + "epon", + "10g-epon", + "gpon", + "xg-pon", + "xgs-pon", + "ng-pon2", + "25g-pon", + "50g-pon", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "cisco-stackwise-80", + "cisco-stackwise-160", + "cisco-stackwise-320", + "cisco-stackwise-480", + "cisco-stackwise-1t", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512", + "other" + ], + "type": "string", + "description": "* `virtual` - Virtual\n* `bridge` - Bridge\n* `lag` - Link Aggregation Group (LAG)\n* `100base-fx` - 100BASE-FX (10/100ME)\n* `100base-lfx` - 100BASE-LFX (10/100ME)\n* `100base-tx` - 100BASE-TX (10/100ME)\n* `100base-t1` - 100BASE-T1 (10/100ME)\n* `1000base-bx10-d` - 1000BASE-BX10-D (1GE BiDi Down)\n* `1000base-bx10-u` - 1000BASE-BX10-U (1GE BiDi Up)\n* `1000base-cwdm` - 1000BASE-CWDM (1GE)\n* `1000base-cx` - 1000BASE-CX (1GE DAC)\n* `1000base-dwdm` - 1000BASE-DWDM (1GE)\n* `1000base-ex` - 1000BASE-EX (1GE)\n* `1000base-lsx` - 1000BASE-LSX (1GE)\n* `1000base-lx` - 1000BASE-LX (1GE)\n* `1000base-lx10` - 1000BASE-LX10/LH (1GE)\n* `1000base-sx` - 1000BASE-SX (1GE)\n* `1000base-t` - 1000BASE-T (1GE)\n* `1000base-tx` - 1000BASE-TX (1GE)\n* `1000base-zx` - 1000BASE-ZX (1GE)\n* `2.5gbase-t` - 2.5GBASE-T (2.5GE)\n* `5gbase-t` - 5GBASE-T (5GE)\n* `10gbase-br-d` - 10GBASE-BR-D (10GE BiDi Down)\n* `10gbase-br-u` - 10GBASE-BR-U (10GE BiDi Up)\n* `10gbase-cu` - 10GBASE-CU (10GE DAC Passive Twinax)\n* `10gbase-cx4` - 10GBASE-CX4 (10GE DAC)\n* `10gbase-er` - 10GBASE-ER (10GE)\n* `10gbase-lr` - 10GBASE-LR (10GE)\n* `10gbase-lrm` - 10GBASE-LRM (10GE)\n* `10gbase-lx4` - 10GBASE-LX4 (10GE)\n* `10gbase-sr` - 10GBASE-SR (10GE)\n* `10gbase-t` - 10GBASE-T (10GE)\n* `10gbase-zr` - 10GBASE-ZR (10GE)\n* `25gbase-cr` - 25GBASE-CR (25GE DAC)\n* `25gbase-er` - 25GBASE-ER (25GE)\n* `25gbase-lr` - 25GBASE-LR (25GE)\n* `25gbase-sr` - 25GBASE-SR (25GE)\n* `25gbase-t` - 25GBASE-T (25GE)\n* `40gbase-cr4` - 40GBASE-CR4 (40GE DAC)\n* `40gbase-er4` - 40GBASE-ER4 (40GE)\n* `40gbase-fr4` - 40GBASE-FR4 (40GE)\n* `40gbase-lr4` - 40GBASE-LR4 (40GE)\n* `40gbase-sr4` - 40GBASE-SR4 (40GE)\n* `40gbase-sr4-bd` - 40GBASE-SR4 (40GE BiDi)\n* `50gbase-cr` - 50GBASE-CR (50GE DAC)\n* `50gbase-er` - 50GBASE-ER (50GE)\n* `50gbase-fr` - 50GBASE-FR (50GE)\n* `50gbase-lr` - 50GBASE-LR (50GE)\n* `50gbase-sr` - 50GBASE-SR (50GE)\n* `100gbase-cr1` - 100GBASE-CR1 (100GE DAC)\n* `100gbase-cr2` - 100GBASE-CR2 (100GE DAC)\n* `100gbase-cr4` - 100GBASE-CR4 (100GE DAC)\n* `100gbase-cr10` - 100GBASE-CR10 (100GE DAC)\n* `100gbase-cwdm4` - 100GBASE-CWDM4 (100GE)\n* `100gbase-dr` - 100GBASE-DR (100GE)\n* `100gbase-er4` - 100GBASE-ER4 (100GE)\n* `100gbase-fr1` - 100GBASE-FR1 (100GE)\n* `100gbase-lr1` - 100GBASE-LR1 (100GE)\n* `100gbase-lr4` - 100GBASE-LR4 (100GE)\n* `100gbase-sr1` - 100GBASE-SR1 (100GE)\n* `100gbase-sr1.2` - 100GBASE-SR1.2 (100GE BiDi)\n* `100gbase-sr2` - 100GBASE-SR2 (100GE)\n* `100gbase-sr4` - 100GBASE-SR4 (100GE)\n* `100gbase-sr10` - 100GBASE-SR10 (100GE)\n* `100gbase-zr` - 100GBASE-ZR (100GE)\n* `200gbase-cr2` - 200GBASE-CR2 (200GE)\n* `200gbase-cr4` - 200GBASE-CR4 (200GE)\n* `200gbase-dr4` - 200GBASE-DR4 (200GE)\n* `200gbase-er4` - 200GBASE-ER4 (200GE)\n* `200gbase-fr4` - 200GBASE-FR4 (200GE)\n* `200gbase-lr4` - 200GBASE-LR4 (200GE)\n* `200gbase-sr2` - 200GBASE-SR2 (200GE)\n* `200gbase-sr4` - 200GBASE-SR4 (200GE)\n* `200gbase-vr2` - 200GBASE-VR2 (200GE)\n* `400gbase-cr4` - 400GBASE-CR4 (400GE)\n* `400gbase-dr4` - 400GBASE-DR4 (400GE)\n* `400gbase-er8` - 400GBASE-ER8 (400GE)\n* `400gbase-fr4` - 400GBASE-FR4 (400GE)\n* `400gbase-fr8` - 400GBASE-FR8 (400GE)\n* `400gbase-lr4` - 400GBASE-LR4 (400GE)\n* `400gbase-lr8` - 400GBASE-LR8 (400GE)\n* `400gbase-sr4` - 400GBASE-SR4 (400GE)\n* `400gbase-sr4_2` - 400GBASE-SR4.2 (400GE BiDi)\n* `400gbase-sr8` - 400GBASE-SR8 (400GE)\n* `400gbase-sr16` - 400GBASE-SR16 (400GE)\n* `400gbase-vr4` - 400GBASE-VR4 (400GE)\n* `400gbase-zr` - 400GBASE-ZR (400GE)\n* `800gbase-cr8` - 800GBASE-CR8 (800GE)\n* `800gbase-dr8` - 800GBASE-DR8 (800GE)\n* `800gbase-sr8` - 800GBASE-SR8 (800GE)\n* `800gbase-vr8` - 800GBASE-VR8 (800GE)\n* `1.6tbase-cr8` - 1.6TBASE-CR8 (1.6TE)\n* `1.6tbase-dr8` - 1.6TBASE-DR8 (1.6TE)\n* `1.6tbase-dr8-2` - 1.6TBASE-DR8-2 (1.6TE)\n* `100base-x-sfp` - SFP (100ME)\n* `1000base-x-gbic` - GBIC (1GE)\n* `1000base-x-sfp` - SFP (1GE)\n* `2.5gbase-x-sfp` - SFP (2.5GE)\n* `10gbase-x-sfpp` - SFP+ (10GE)\n* `10gbase-x-xenpak` - XENPAK (10GE)\n* `10gbase-x-xfp` - XFP (10GE)\n* `10gbase-x-x2` - X2 (10GE)\n* `25gbase-x-sfp28` - SFP28 (25GE)\n* `40gbase-x-qsfpp` - QSFP+ (40GE)\n* `50gbase-x-sfp28` - QSFP28 (50GE)\n* `50gbase-x-sfp56` - SFP56 (50GE)\n* `100gbase-x-cfp` - CFP (100GE)\n* `100gbase-x-cfp2` - CFP2 (100GE)\n* `100gbase-x-cfp4` - CFP4 (100GE)\n* `100gbase-x-cxp` - CXP (100GE)\n* `100gbase-x-cpak` - Cisco CPAK (100GE)\n* `100gbase-x-dsfp` - DSFP (100GE)\n* `100gbase-x-qsfp28` - QSFP28 (100GE)\n* `100gbase-x-qsfpdd` - QSFP-DD (100GE)\n* `100gbase-x-sfpdd` - SFP-DD (100GE)\n* `200gbase-x-cfp2` - CFP2 (200GE)\n* `200gbase-x-qsfp56` - QSFP56 (200GE)\n* `200gbase-x-qsfpdd` - QSFP-DD (200GE)\n* `400gbase-x-qsfp112` - QSFP112 (400GE)\n* `400gbase-x-qsfpdd` - QSFP-DD (400GE)\n* `400gbase-x-cdfp` - CDFP (400GE)\n* `400gbase-x-cfp2` - CFP2 (400GE)\n* `400gbase-x-cfp8` - CPF8 (400GE)\n* `400gbase-x-osfp` - OSFP (400GE)\n* `400gbase-x-osfp-rhs` - OSFP-RHS (400GE)\n* `800gbase-x-osfp` - OSFP (800GE)\n* `800gbase-x-qsfpdd` - QSFP-DD (800GE)\n* `1.6tbase-x-osfp1600` - OSFP1600 (1.6TE)\n* `1.6tbase-x-osfp1600-rhs` - OSFP1600-RHS (1.6TE)\n* `1.6tbase-x-qsfpdd1600` - QSFP-DD1600 (1.6TE)\n* `1000base-kx` - 1000BASE-KX (1GE)\n* `2.5gbase-kx` - 2.5GBASE-KX (2.5GE)\n* `5gbase-kr` - 5GBASE-KR (5GE)\n* `10gbase-kr` - 10GBASE-KR (10GE)\n* `10gbase-kx4` - 10GBASE-KX4 (10GE)\n* `25gbase-kr` - 25GBASE-KR (25GE)\n* `40gbase-kr4` - 40GBASE-KR4 (40GE)\n* `50gbase-kr` - 50GBASE-KR (50GE)\n* `100gbase-kp4` - 100GBASE-KP4 (100GE)\n* `100gbase-kr2` - 100GBASE-KR2 (100GE)\n* `100gbase-kr4` - 100GBASE-KR4 (100GE)\n* `1.6tbase-kr8` - 1.6TBASE-KR8 (1.6TE)\n* `ieee802.11a` - IEEE 802.11a\n* `ieee802.11g` - IEEE 802.11b/g\n* `ieee802.11n` - IEEE 802.11n (Wi-Fi 4)\n* `ieee802.11ac` - IEEE 802.11ac (Wi-Fi 5)\n* `ieee802.11ad` - IEEE 802.11ad (WiGig)\n* `ieee802.11ax` - IEEE 802.11ax (Wi-Fi 6)\n* `ieee802.11ay` - IEEE 802.11ay (WiGig)\n* `ieee802.11be` - IEEE 802.11be (Wi-Fi 7)\n* `ieee802.15.1` - IEEE 802.15.1 (Bluetooth)\n* `ieee802.15.4` - IEEE 802.15.4 (LR-WPAN)\n* `other-wireless` - Other (Wireless)\n* `gsm` - GSM\n* `cdma` - CDMA\n* `lte` - LTE\n* `4g` - 4G\n* `5g` - 5G\n* `sonet-oc3` - OC-3/STM-1\n* `sonet-oc12` - OC-12/STM-4\n* `sonet-oc48` - OC-48/STM-16\n* `sonet-oc192` - OC-192/STM-64\n* `sonet-oc768` - OC-768/STM-256\n* `sonet-oc1920` - OC-1920/STM-640\n* `sonet-oc3840` - OC-3840/STM-1234\n* `1gfc-sfp` - SFP (1GFC)\n* `2gfc-sfp` - SFP (2GFC)\n* `4gfc-sfp` - SFP (4GFC)\n* `8gfc-sfpp` - SFP+ (8GFC)\n* `16gfc-sfpp` - SFP+ (16GFC)\n* `32gfc-sfp28` - SFP28 (32GFC)\n* `32gfc-sfpp` - SFP+ (32GFC)\n* `64gfc-qsfpp` - QSFP+ (64GFC)\n* `64gfc-sfpdd` - SFP-DD (64GFC)\n* `64gfc-sfpp` - SFP+ (64GFC)\n* `128gfc-qsfp28` - QSFP28 (128GFC)\n* `infiniband-sdr` - SDR (2 Gbps)\n* `infiniband-ddr` - DDR (4 Gbps)\n* `infiniband-qdr` - QDR (8 Gbps)\n* `infiniband-fdr10` - FDR10 (10 Gbps)\n* `infiniband-fdr` - FDR (13.5 Gbps)\n* `infiniband-edr` - EDR (25 Gbps)\n* `infiniband-hdr` - HDR (50 Gbps)\n* `infiniband-ndr` - NDR (100 Gbps)\n* `infiniband-xdr` - XDR (250 Gbps)\n* `t1` - T1 (1.544 Mbps)\n* `e1` - E1 (2.048 Mbps)\n* `t3` - T3 (45 Mbps)\n* `e3` - E3 (34 Mbps)\n* `xdsl` - xDSL\n* `docsis` - DOCSIS\n* `moca` - MoCA\n* `bpon` - BPON (622 Mbps / 155 Mbps)\n* `epon` - EPON (1 Gbps)\n* `10g-epon` - 10G-EPON (10 Gbps)\n* `gpon` - GPON (2.5 Gbps / 1.25 Gbps)\n* `xg-pon` - XG-PON (10 Gbps / 2.5 Gbps)\n* `xgs-pon` - XGS-PON (10 Gbps)\n* `ng-pon2` - NG-PON2 (TWDM-PON) (4x10 Gbps)\n* `25g-pon` - 25G-PON (25 Gbps)\n* `50g-pon` - 50G-PON (50 Gbps)\n* `cisco-stackwise` - Cisco StackWise\n* `cisco-stackwise-plus` - Cisco StackWise Plus\n* `cisco-flexstack` - Cisco FlexStack\n* `cisco-flexstack-plus` - Cisco FlexStack Plus\n* `cisco-stackwise-80` - Cisco StackWise-80\n* `cisco-stackwise-160` - Cisco StackWise-160\n* `cisco-stackwise-320` - Cisco StackWise-320\n* `cisco-stackwise-480` - Cisco StackWise-480\n* `cisco-stackwise-1t` - Cisco StackWise-1T\n* `juniper-vcp` - Juniper VCP\n* `extreme-summitstack` - Extreme SummitStack\n* `extreme-summitstack-128` - Extreme SummitStack-128\n* `extreme-summitstack-256` - Extreme SummitStack-256\n* `extreme-summitstack-512` - Extreme SummitStack-512\n* `other` - Other", + "x-spec-enum-id": "b067eb1f050c6ae9" + }, + "enabled": { + "type": "boolean" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceRequest" + } + ], + "nullable": true + }, + "bridge": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceRequest" + } + ], + "nullable": true + }, + "lag": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceRequest" + } + ], + "nullable": true + }, + "mtu": { + "type": "integer", + "maximum": 65536, + "minimum": 1, + "nullable": true + }, + "primary_mac_address": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefMACAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "speed": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true, + "title": "Speed (Kbps)" + }, + "duplex": { + "enum": [ + "half", + "full", + "auto", + "", + null + ], + "type": "string", + "description": "* `half` - Half\n* `full` - Full\n* `auto` - Auto", + "x-spec-enum-id": "368458a2b67c916b", + "nullable": true + }, + "wwn": { + "type": "string", + "nullable": true + }, + "mgmt_only": { + "type": "boolean", + "title": "Management only", + "description": "This interface is used only for out-of-band management" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mode": { + "enum": [ + "access", + "tagged", + "tagged-all", + "q-in-q", + "" + ], + "type": "string", + "description": "* `access` - Access\n* `tagged` - Tagged\n* `tagged-all` - Tagged (All)\n* `q-in-q` - Q-in-Q (802.1ad)", + "x-spec-enum-id": "84129b71b974ebe5" + }, + "rf_role": { + "enum": [ + "ap", + "station", + "" + ], + "type": "string", + "description": "* `ap` - Access point\n* `station` - Station", + "x-spec-enum-id": "d2772dbea88b0fb1" + }, + "rf_channel": { + "enum": [ + "2.4g-1-2412-22", + "2.4g-2-2417-22", + "2.4g-3-2422-22", + "2.4g-4-2427-22", + "2.4g-5-2432-22", + "2.4g-6-2437-22", + "2.4g-7-2442-22", + "2.4g-8-2447-22", + "2.4g-9-2452-22", + "2.4g-10-2457-22", + "2.4g-11-2462-22", + "2.4g-12-2467-22", + "2.4g-13-2472-22", + "5g-32-5160-20", + "5g-34-5170-40", + "5g-36-5180-20", + "5g-38-5190-40", + "5g-40-5200-20", + "5g-42-5210-80", + "5g-44-5220-20", + "5g-46-5230-40", + "5g-48-5240-20", + "5g-50-5250-160", + "5g-52-5260-20", + "5g-54-5270-40", + "5g-56-5280-20", + "5g-58-5290-80", + "5g-60-5300-20", + "5g-62-5310-40", + "5g-64-5320-20", + "5g-100-5500-20", + "5g-102-5510-40", + "5g-104-5520-20", + "5g-106-5530-80", + "5g-108-5540-20", + "5g-110-5550-40", + "5g-112-5560-20", + "5g-114-5570-160", + "5g-116-5580-20", + "5g-118-5590-40", + "5g-120-5600-20", + "5g-122-5610-80", + "5g-124-5620-20", + "5g-126-5630-40", + "5g-128-5640-20", + "5g-132-5660-20", + "5g-134-5670-40", + "5g-136-5680-20", + "5g-138-5690-80", + "5g-140-5700-20", + "5g-142-5710-40", + "5g-144-5720-20", + "5g-149-5745-20", + "5g-151-5755-40", + "5g-153-5765-20", + "5g-155-5775-80", + "5g-157-5785-20", + "5g-159-5795-40", + "5g-161-5805-20", + "5g-163-5815-160", + "5g-165-5825-20", + "5g-167-5835-40", + "5g-169-5845-20", + "5g-171-5855-80", + "5g-173-5865-20", + "5g-175-5875-40", + "5g-177-5885-20", + "6g-1-5955-20", + "6g-3-5965-40", + "6g-5-5975-20", + "6g-7-5985-80", + "6g-9-5995-20", + "6g-11-6005-40", + "6g-13-6015-20", + "6g-15-6025-160", + "6g-17-6035-20", + "6g-19-6045-40", + "6g-21-6055-20", + "6g-23-6065-80", + "6g-25-6075-20", + "6g-27-6085-40", + "6g-29-6095-20", + "6g-31-6105-320", + "6g-33-6115-20", + "6g-35-6125-40", + "6g-37-6135-20", + "6g-39-6145-80", + "6g-41-6155-20", + "6g-43-6165-40", + "6g-45-6175-20", + "6g-47-6185-160", + "6g-49-6195-20", + "6g-51-6205-40", + "6g-53-6215-20", + "6g-55-6225-80", + "6g-57-6235-20", + "6g-59-6245-40", + "6g-61-6255-20", + "6g-65-6275-20", + "6g-67-6285-40", + "6g-69-6295-20", + "6g-71-6305-80", + "6g-73-6315-20", + "6g-75-6325-40", + "6g-77-6335-20", + "6g-79-6345-160", + "6g-81-6355-20", + "6g-83-6365-40", + "6g-85-6375-20", + "6g-87-6385-80", + "6g-89-6395-20", + "6g-91-6405-40", + "6g-93-6415-20", + "6g-95-6425-320", + "6g-97-6435-20", + "6g-99-6445-40", + "6g-101-6455-20", + "6g-103-6465-80", + "6g-105-6475-20", + "6g-107-6485-40", + "6g-109-6495-20", + "6g-111-6505-160", + "6g-113-6515-20", + "6g-115-6525-40", + "6g-117-6535-20", + "6g-119-6545-80", + "6g-121-6555-20", + "6g-123-6565-40", + "6g-125-6575-20", + "6g-129-6595-20", + "6g-131-6605-40", + "6g-133-6615-20", + "6g-135-6625-80", + "6g-137-6635-20", + "6g-139-6645-40", + "6g-141-6655-20", + "6g-143-6665-160", + "6g-145-6675-20", + "6g-147-6685-40", + "6g-149-6695-20", + "6g-151-6705-80", + "6g-153-6715-20", + "6g-155-6725-40", + "6g-157-6735-20", + "6g-159-6745-320", + "6g-161-6755-20", + "6g-163-6765-40", + "6g-165-6775-20", + "6g-167-6785-80", + "6g-169-6795-20", + "6g-171-6805-40", + "6g-173-6815-20", + "6g-175-6825-160", + "6g-177-6835-20", + "6g-179-6845-40", + "6g-181-6855-20", + "6g-183-6865-80", + "6g-185-6875-20", + "6g-187-6885-40", + "6g-189-6895-20", + "6g-193-6915-20", + "6g-195-6925-40", + "6g-197-6935-20", + "6g-199-6945-80", + "6g-201-6955-20", + "6g-203-6965-40", + "6g-205-6975-20", + "6g-207-6985-160", + "6g-209-6995-20", + "6g-211-7005-40", + "6g-213-7015-20", + "6g-215-7025-80", + "6g-217-7035-20", + "6g-219-7045-40", + "6g-221-7055-20", + "6g-225-7075-20", + "6g-227-7085-40", + "6g-229-7095-20", + "6g-233-7115-20", + "60g-1-58320-2160", + "60g-2-60480-2160", + "60g-3-62640-2160", + "60g-4-64800-2160", + "60g-5-66960-2160", + "60g-6-69120-2160", + "60g-9-59400-4320", + "60g-10-61560-4320", + "60g-11-63720-4320", + "60g-12-65880-4320", + "60g-13-68040-4320", + "60g-17-60480-6480", + "60g-18-62640-6480", + "60g-19-64800-6480", + "60g-20-66960-6480", + "60g-25-61560-6480", + "60g-26-63720-6480", + "60g-27-65880-6480", + "" + ], + "type": "string", + "description": "* `2.4g-1-2412-22` - 1 (2412 MHz)\n* `2.4g-2-2417-22` - 2 (2417 MHz)\n* `2.4g-3-2422-22` - 3 (2422 MHz)\n* `2.4g-4-2427-22` - 4 (2427 MHz)\n* `2.4g-5-2432-22` - 5 (2432 MHz)\n* `2.4g-6-2437-22` - 6 (2437 MHz)\n* `2.4g-7-2442-22` - 7 (2442 MHz)\n* `2.4g-8-2447-22` - 8 (2447 MHz)\n* `2.4g-9-2452-22` - 9 (2452 MHz)\n* `2.4g-10-2457-22` - 10 (2457 MHz)\n* `2.4g-11-2462-22` - 11 (2462 MHz)\n* `2.4g-12-2467-22` - 12 (2467 MHz)\n* `2.4g-13-2472-22` - 13 (2472 MHz)\n* `5g-32-5160-20` - 32 (5160/20 MHz)\n* `5g-34-5170-40` - 34 (5170/40 MHz)\n* `5g-36-5180-20` - 36 (5180/20 MHz)\n* `5g-38-5190-40` - 38 (5190/40 MHz)\n* `5g-40-5200-20` - 40 (5200/20 MHz)\n* `5g-42-5210-80` - 42 (5210/80 MHz)\n* `5g-44-5220-20` - 44 (5220/20 MHz)\n* `5g-46-5230-40` - 46 (5230/40 MHz)\n* `5g-48-5240-20` - 48 (5240/20 MHz)\n* `5g-50-5250-160` - 50 (5250/160 MHz)\n* `5g-52-5260-20` - 52 (5260/20 MHz)\n* `5g-54-5270-40` - 54 (5270/40 MHz)\n* `5g-56-5280-20` - 56 (5280/20 MHz)\n* `5g-58-5290-80` - 58 (5290/80 MHz)\n* `5g-60-5300-20` - 60 (5300/20 MHz)\n* `5g-62-5310-40` - 62 (5310/40 MHz)\n* `5g-64-5320-20` - 64 (5320/20 MHz)\n* `5g-100-5500-20` - 100 (5500/20 MHz)\n* `5g-102-5510-40` - 102 (5510/40 MHz)\n* `5g-104-5520-20` - 104 (5520/20 MHz)\n* `5g-106-5530-80` - 106 (5530/80 MHz)\n* `5g-108-5540-20` - 108 (5540/20 MHz)\n* `5g-110-5550-40` - 110 (5550/40 MHz)\n* `5g-112-5560-20` - 112 (5560/20 MHz)\n* `5g-114-5570-160` - 114 (5570/160 MHz)\n* `5g-116-5580-20` - 116 (5580/20 MHz)\n* `5g-118-5590-40` - 118 (5590/40 MHz)\n* `5g-120-5600-20` - 120 (5600/20 MHz)\n* `5g-122-5610-80` - 122 (5610/80 MHz)\n* `5g-124-5620-20` - 124 (5620/20 MHz)\n* `5g-126-5630-40` - 126 (5630/40 MHz)\n* `5g-128-5640-20` - 128 (5640/20 MHz)\n* `5g-132-5660-20` - 132 (5660/20 MHz)\n* `5g-134-5670-40` - 134 (5670/40 MHz)\n* `5g-136-5680-20` - 136 (5680/20 MHz)\n* `5g-138-5690-80` - 138 (5690/80 MHz)\n* `5g-140-5700-20` - 140 (5700/20 MHz)\n* `5g-142-5710-40` - 142 (5710/40 MHz)\n* `5g-144-5720-20` - 144 (5720/20 MHz)\n* `5g-149-5745-20` - 149 (5745/20 MHz)\n* `5g-151-5755-40` - 151 (5755/40 MHz)\n* `5g-153-5765-20` - 153 (5765/20 MHz)\n* `5g-155-5775-80` - 155 (5775/80 MHz)\n* `5g-157-5785-20` - 157 (5785/20 MHz)\n* `5g-159-5795-40` - 159 (5795/40 MHz)\n* `5g-161-5805-20` - 161 (5805/20 MHz)\n* `5g-163-5815-160` - 163 (5815/160 MHz)\n* `5g-165-5825-20` - 165 (5825/20 MHz)\n* `5g-167-5835-40` - 167 (5835/40 MHz)\n* `5g-169-5845-20` - 169 (5845/20 MHz)\n* `5g-171-5855-80` - 171 (5855/80 MHz)\n* `5g-173-5865-20` - 173 (5865/20 MHz)\n* `5g-175-5875-40` - 175 (5875/40 MHz)\n* `5g-177-5885-20` - 177 (5885/20 MHz)\n* `6g-1-5955-20` - 1 (5955/20 MHz)\n* `6g-3-5965-40` - 3 (5965/40 MHz)\n* `6g-5-5975-20` - 5 (5975/20 MHz)\n* `6g-7-5985-80` - 7 (5985/80 MHz)\n* `6g-9-5995-20` - 9 (5995/20 MHz)\n* `6g-11-6005-40` - 11 (6005/40 MHz)\n* `6g-13-6015-20` - 13 (6015/20 MHz)\n* `6g-15-6025-160` - 15 (6025/160 MHz)\n* `6g-17-6035-20` - 17 (6035/20 MHz)\n* `6g-19-6045-40` - 19 (6045/40 MHz)\n* `6g-21-6055-20` - 21 (6055/20 MHz)\n* `6g-23-6065-80` - 23 (6065/80 MHz)\n* `6g-25-6075-20` - 25 (6075/20 MHz)\n* `6g-27-6085-40` - 27 (6085/40 MHz)\n* `6g-29-6095-20` - 29 (6095/20 MHz)\n* `6g-31-6105-320` - 31 (6105/320 MHz)\n* `6g-33-6115-20` - 33 (6115/20 MHz)\n* `6g-35-6125-40` - 35 (6125/40 MHz)\n* `6g-37-6135-20` - 37 (6135/20 MHz)\n* `6g-39-6145-80` - 39 (6145/80 MHz)\n* `6g-41-6155-20` - 41 (6155/20 MHz)\n* `6g-43-6165-40` - 43 (6165/40 MHz)\n* `6g-45-6175-20` - 45 (6175/20 MHz)\n* `6g-47-6185-160` - 47 (6185/160 MHz)\n* `6g-49-6195-20` - 49 (6195/20 MHz)\n* `6g-51-6205-40` - 51 (6205/40 MHz)\n* `6g-53-6215-20` - 53 (6215/20 MHz)\n* `6g-55-6225-80` - 55 (6225/80 MHz)\n* `6g-57-6235-20` - 57 (6235/20 MHz)\n* `6g-59-6245-40` - 59 (6245/40 MHz)\n* `6g-61-6255-20` - 61 (6255/20 MHz)\n* `6g-65-6275-20` - 65 (6275/20 MHz)\n* `6g-67-6285-40` - 67 (6285/40 MHz)\n* `6g-69-6295-20` - 69 (6295/20 MHz)\n* `6g-71-6305-80` - 71 (6305/80 MHz)\n* `6g-73-6315-20` - 73 (6315/20 MHz)\n* `6g-75-6325-40` - 75 (6325/40 MHz)\n* `6g-77-6335-20` - 77 (6335/20 MHz)\n* `6g-79-6345-160` - 79 (6345/160 MHz)\n* `6g-81-6355-20` - 81 (6355/20 MHz)\n* `6g-83-6365-40` - 83 (6365/40 MHz)\n* `6g-85-6375-20` - 85 (6375/20 MHz)\n* `6g-87-6385-80` - 87 (6385/80 MHz)\n* `6g-89-6395-20` - 89 (6395/20 MHz)\n* `6g-91-6405-40` - 91 (6405/40 MHz)\n* `6g-93-6415-20` - 93 (6415/20 MHz)\n* `6g-95-6425-320` - 95 (6425/320 MHz)\n* `6g-97-6435-20` - 97 (6435/20 MHz)\n* `6g-99-6445-40` - 99 (6445/40 MHz)\n* `6g-101-6455-20` - 101 (6455/20 MHz)\n* `6g-103-6465-80` - 103 (6465/80 MHz)\n* `6g-105-6475-20` - 105 (6475/20 MHz)\n* `6g-107-6485-40` - 107 (6485/40 MHz)\n* `6g-109-6495-20` - 109 (6495/20 MHz)\n* `6g-111-6505-160` - 111 (6505/160 MHz)\n* `6g-113-6515-20` - 113 (6515/20 MHz)\n* `6g-115-6525-40` - 115 (6525/40 MHz)\n* `6g-117-6535-20` - 117 (6535/20 MHz)\n* `6g-119-6545-80` - 119 (6545/80 MHz)\n* `6g-121-6555-20` - 121 (6555/20 MHz)\n* `6g-123-6565-40` - 123 (6565/40 MHz)\n* `6g-125-6575-20` - 125 (6575/20 MHz)\n* `6g-129-6595-20` - 129 (6595/20 MHz)\n* `6g-131-6605-40` - 131 (6605/40 MHz)\n* `6g-133-6615-20` - 133 (6615/20 MHz)\n* `6g-135-6625-80` - 135 (6625/80 MHz)\n* `6g-137-6635-20` - 137 (6635/20 MHz)\n* `6g-139-6645-40` - 139 (6645/40 MHz)\n* `6g-141-6655-20` - 141 (6655/20 MHz)\n* `6g-143-6665-160` - 143 (6665/160 MHz)\n* `6g-145-6675-20` - 145 (6675/20 MHz)\n* `6g-147-6685-40` - 147 (6685/40 MHz)\n* `6g-149-6695-20` - 149 (6695/20 MHz)\n* `6g-151-6705-80` - 151 (6705/80 MHz)\n* `6g-153-6715-20` - 153 (6715/20 MHz)\n* `6g-155-6725-40` - 155 (6725/40 MHz)\n* `6g-157-6735-20` - 157 (6735/20 MHz)\n* `6g-159-6745-320` - 159 (6745/320 MHz)\n* `6g-161-6755-20` - 161 (6755/20 MHz)\n* `6g-163-6765-40` - 163 (6765/40 MHz)\n* `6g-165-6775-20` - 165 (6775/20 MHz)\n* `6g-167-6785-80` - 167 (6785/80 MHz)\n* `6g-169-6795-20` - 169 (6795/20 MHz)\n* `6g-171-6805-40` - 171 (6805/40 MHz)\n* `6g-173-6815-20` - 173 (6815/20 MHz)\n* `6g-175-6825-160` - 175 (6825/160 MHz)\n* `6g-177-6835-20` - 177 (6835/20 MHz)\n* `6g-179-6845-40` - 179 (6845/40 MHz)\n* `6g-181-6855-20` - 181 (6855/20 MHz)\n* `6g-183-6865-80` - 183 (6865/80 MHz)\n* `6g-185-6875-20` - 185 (6875/20 MHz)\n* `6g-187-6885-40` - 187 (6885/40 MHz)\n* `6g-189-6895-20` - 189 (6895/20 MHz)\n* `6g-193-6915-20` - 193 (6915/20 MHz)\n* `6g-195-6925-40` - 195 (6925/40 MHz)\n* `6g-197-6935-20` - 197 (6935/20 MHz)\n* `6g-199-6945-80` - 199 (6945/80 MHz)\n* `6g-201-6955-20` - 201 (6955/20 MHz)\n* `6g-203-6965-40` - 203 (6965/40 MHz)\n* `6g-205-6975-20` - 205 (6975/20 MHz)\n* `6g-207-6985-160` - 207 (6985/160 MHz)\n* `6g-209-6995-20` - 209 (6995/20 MHz)\n* `6g-211-7005-40` - 211 (7005/40 MHz)\n* `6g-213-7015-20` - 213 (7015/20 MHz)\n* `6g-215-7025-80` - 215 (7025/80 MHz)\n* `6g-217-7035-20` - 217 (7035/20 MHz)\n* `6g-219-7045-40` - 219 (7045/40 MHz)\n* `6g-221-7055-20` - 221 (7055/20 MHz)\n* `6g-225-7075-20` - 225 (7075/20 MHz)\n* `6g-227-7085-40` - 227 (7085/40 MHz)\n* `6g-229-7095-20` - 229 (7095/20 MHz)\n* `6g-233-7115-20` - 233 (7115/20 MHz)\n* `60g-1-58320-2160` - 1 (58.32/2.16 GHz)\n* `60g-2-60480-2160` - 2 (60.48/2.16 GHz)\n* `60g-3-62640-2160` - 3 (62.64/2.16 GHz)\n* `60g-4-64800-2160` - 4 (64.80/2.16 GHz)\n* `60g-5-66960-2160` - 5 (66.96/2.16 GHz)\n* `60g-6-69120-2160` - 6 (69.12/2.16 GHz)\n* `60g-9-59400-4320` - 9 (59.40/4.32 GHz)\n* `60g-10-61560-4320` - 10 (61.56/4.32 GHz)\n* `60g-11-63720-4320` - 11 (63.72/4.32 GHz)\n* `60g-12-65880-4320` - 12 (65.88/4.32 GHz)\n* `60g-13-68040-4320` - 13 (68.04/4.32 GHz)\n* `60g-17-60480-6480` - 17 (60.48/6.48 GHz)\n* `60g-18-62640-6480` - 18 (62.64/6.48 GHz)\n* `60g-19-64800-6480` - 19 (64.80/6.48 GHz)\n* `60g-20-66960-6480` - 20 (66.96/6.48 GHz)\n* `60g-25-61560-6480` - 25 (61.56/8.64 GHz)\n* `60g-26-63720-6480` - 26 (63.72/8.64 GHz)\n* `60g-27-65880-6480` - 27 (65.88/8.64 GHz)", + "x-spec-enum-id": "70cf66176c475063" + }, + "poe_mode": { + "enum": [ + "pd", + "pse", + "" + ], + "type": "string", + "description": "* `pd` - PD\n* `pse` - PSE", + "x-spec-enum-id": "2f2fe6dcdc7772bd" + }, + "poe_type": { + "enum": [ + "type1-ieee802.3af", + "type2-ieee802.3at", + "type3-ieee802.3bt", + "type4-ieee802.3bt", + "passive-24v-2pair", + "passive-24v-4pair", + "passive-48v-2pair", + "passive-48v-4pair", + "" + ], + "type": "string", + "description": "* `type1-ieee802.3af` - 802.3af (Type 1)\n* `type2-ieee802.3at` - 802.3at (Type 2)\n* `type3-ieee802.3bt` - 802.3bt (Type 3)\n* `type4-ieee802.3bt` - 802.3bt (Type 4)\n* `passive-24v-2pair` - Passive 24V (2-pair)\n* `passive-24v-4pair` - Passive 24V (4-pair)\n* `passive-48v-2pair` - Passive 48V (2-pair)\n* `passive-48v-4pair` - Passive 48V (4-pair)", + "x-spec-enum-id": "5473d57885f237ab" + }, + "rf_channel_frequency": { + "type": "number", + "format": "double", + "maximum": 100000, + "minimum": -100000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Channel frequency (MHz)", + "description": "Populated by selected channel (if set)" + }, + "rf_channel_width": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": -10000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Channel width (MHz)", + "description": "Populated by selected channel (if set)" + }, + "tx_power": { + "type": "integer", + "maximum": 127, + "minimum": -40, + "nullable": true, + "title": "Transmit power (dBm)" + }, + "untagged_vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tagged_vlans": { + "type": "array", + "items": { + "type": "integer" + } + }, + "qinq_svlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vlan_translation_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANTranslationPolicyRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "wireless_lans": { + "type": "array", + "items": { + "type": "integer" + } + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name", + "type" + ] + }, + "BulkInterfaceTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "virtual", + "bridge", + "lag", + "100base-fx", + "100base-lfx", + "100base-tx", + "100base-t1", + "1000base-bx10-d", + "1000base-bx10-u", + "1000base-cwdm", + "1000base-cx", + "1000base-dwdm", + "1000base-ex", + "1000base-lsx", + "1000base-lx", + "1000base-lx10", + "1000base-sx", + "1000base-t", + "1000base-tx", + "1000base-zx", + "2.5gbase-t", + "5gbase-t", + "10gbase-br-d", + "10gbase-br-u", + "10gbase-cu", + "10gbase-cx4", + "10gbase-er", + "10gbase-lr", + "10gbase-lrm", + "10gbase-lx4", + "10gbase-sr", + "10gbase-t", + "10gbase-zr", + "25gbase-cr", + "25gbase-er", + "25gbase-lr", + "25gbase-sr", + "25gbase-t", + "40gbase-cr4", + "40gbase-er4", + "40gbase-fr4", + "40gbase-lr4", + "40gbase-sr4", + "40gbase-sr4-bd", + "50gbase-cr", + "50gbase-er", + "50gbase-fr", + "50gbase-lr", + "50gbase-sr", + "100gbase-cr1", + "100gbase-cr2", + "100gbase-cr4", + "100gbase-cr10", + "100gbase-cwdm4", + "100gbase-dr", + "100gbase-er4", + "100gbase-fr1", + "100gbase-lr1", + "100gbase-lr4", + "100gbase-sr1", + "100gbase-sr1.2", + "100gbase-sr2", + "100gbase-sr4", + "100gbase-sr10", + "100gbase-zr", + "200gbase-cr2", + "200gbase-cr4", + "200gbase-dr4", + "200gbase-er4", + "200gbase-fr4", + "200gbase-lr4", + "200gbase-sr2", + "200gbase-sr4", + "200gbase-vr2", + "400gbase-cr4", + "400gbase-dr4", + "400gbase-er8", + "400gbase-fr4", + "400gbase-fr8", + "400gbase-lr4", + "400gbase-lr8", + "400gbase-sr4", + "400gbase-sr4_2", + "400gbase-sr8", + "400gbase-sr16", + "400gbase-vr4", + "400gbase-zr", + "800gbase-cr8", + "800gbase-dr8", + "800gbase-sr8", + "800gbase-vr8", + "1.6tbase-cr8", + "1.6tbase-dr8", + "1.6tbase-dr8-2", + "100base-x-sfp", + "1000base-x-gbic", + "1000base-x-sfp", + "2.5gbase-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xenpak", + "10gbase-x-xfp", + "10gbase-x-x2", + "25gbase-x-sfp28", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "50gbase-x-sfp56", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cxp", + "100gbase-x-cpak", + "100gbase-x-dsfp", + "100gbase-x-qsfp28", + "100gbase-x-qsfpdd", + "100gbase-x-sfpdd", + "200gbase-x-cfp2", + "200gbase-x-qsfp56", + "200gbase-x-qsfpdd", + "400gbase-x-qsfp112", + "400gbase-x-qsfpdd", + "400gbase-x-cdfp", + "400gbase-x-cfp2", + "400gbase-x-cfp8", + "400gbase-x-osfp", + "400gbase-x-osfp-rhs", + "800gbase-x-osfp", + "800gbase-x-qsfpdd", + "1.6tbase-x-osfp1600", + "1.6tbase-x-osfp1600-rhs", + "1.6tbase-x-qsfpdd1600", + "1000base-kx", + "2.5gbase-kx", + "5gbase-kr", + "10gbase-kr", + "10gbase-kx4", + "25gbase-kr", + "40gbase-kr4", + "50gbase-kr", + "100gbase-kp4", + "100gbase-kr2", + "100gbase-kr4", + "1.6tbase-kr8", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "ieee802.11ax", + "ieee802.11ay", + "ieee802.11be", + "ieee802.15.1", + "ieee802.15.4", + "other-wireless", + "gsm", + "cdma", + "lte", + "4g", + "5g", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "32gfc-sfpp", + "64gfc-qsfpp", + "64gfc-sfpdd", + "64gfc-sfpp", + "128gfc-qsfp28", + "infiniband-sdr", + "infiniband-ddr", + "infiniband-qdr", + "infiniband-fdr10", + "infiniband-fdr", + "infiniband-edr", + "infiniband-hdr", + "infiniband-ndr", + "infiniband-xdr", + "t1", + "e1", + "t3", + "e3", + "xdsl", + "docsis", + "moca", + "bpon", + "epon", + "10g-epon", + "gpon", + "xg-pon", + "xgs-pon", + "ng-pon2", + "25g-pon", + "50g-pon", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "cisco-stackwise-80", + "cisco-stackwise-160", + "cisco-stackwise-320", + "cisco-stackwise-480", + "cisco-stackwise-1t", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512", + "other" + ], + "type": "string", + "description": "* `virtual` - Virtual\n* `bridge` - Bridge\n* `lag` - Link Aggregation Group (LAG)\n* `100base-fx` - 100BASE-FX (10/100ME)\n* `100base-lfx` - 100BASE-LFX (10/100ME)\n* `100base-tx` - 100BASE-TX (10/100ME)\n* `100base-t1` - 100BASE-T1 (10/100ME)\n* `1000base-bx10-d` - 1000BASE-BX10-D (1GE BiDi Down)\n* `1000base-bx10-u` - 1000BASE-BX10-U (1GE BiDi Up)\n* `1000base-cwdm` - 1000BASE-CWDM (1GE)\n* `1000base-cx` - 1000BASE-CX (1GE DAC)\n* `1000base-dwdm` - 1000BASE-DWDM (1GE)\n* `1000base-ex` - 1000BASE-EX (1GE)\n* `1000base-lsx` - 1000BASE-LSX (1GE)\n* `1000base-lx` - 1000BASE-LX (1GE)\n* `1000base-lx10` - 1000BASE-LX10/LH (1GE)\n* `1000base-sx` - 1000BASE-SX (1GE)\n* `1000base-t` - 1000BASE-T (1GE)\n* `1000base-tx` - 1000BASE-TX (1GE)\n* `1000base-zx` - 1000BASE-ZX (1GE)\n* `2.5gbase-t` - 2.5GBASE-T (2.5GE)\n* `5gbase-t` - 5GBASE-T (5GE)\n* `10gbase-br-d` - 10GBASE-BR-D (10GE BiDi Down)\n* `10gbase-br-u` - 10GBASE-BR-U (10GE BiDi Up)\n* `10gbase-cu` - 10GBASE-CU (10GE DAC Passive Twinax)\n* `10gbase-cx4` - 10GBASE-CX4 (10GE DAC)\n* `10gbase-er` - 10GBASE-ER (10GE)\n* `10gbase-lr` - 10GBASE-LR (10GE)\n* `10gbase-lrm` - 10GBASE-LRM (10GE)\n* `10gbase-lx4` - 10GBASE-LX4 (10GE)\n* `10gbase-sr` - 10GBASE-SR (10GE)\n* `10gbase-t` - 10GBASE-T (10GE)\n* `10gbase-zr` - 10GBASE-ZR (10GE)\n* `25gbase-cr` - 25GBASE-CR (25GE DAC)\n* `25gbase-er` - 25GBASE-ER (25GE)\n* `25gbase-lr` - 25GBASE-LR (25GE)\n* `25gbase-sr` - 25GBASE-SR (25GE)\n* `25gbase-t` - 25GBASE-T (25GE)\n* `40gbase-cr4` - 40GBASE-CR4 (40GE DAC)\n* `40gbase-er4` - 40GBASE-ER4 (40GE)\n* `40gbase-fr4` - 40GBASE-FR4 (40GE)\n* `40gbase-lr4` - 40GBASE-LR4 (40GE)\n* `40gbase-sr4` - 40GBASE-SR4 (40GE)\n* `40gbase-sr4-bd` - 40GBASE-SR4 (40GE BiDi)\n* `50gbase-cr` - 50GBASE-CR (50GE DAC)\n* `50gbase-er` - 50GBASE-ER (50GE)\n* `50gbase-fr` - 50GBASE-FR (50GE)\n* `50gbase-lr` - 50GBASE-LR (50GE)\n* `50gbase-sr` - 50GBASE-SR (50GE)\n* `100gbase-cr1` - 100GBASE-CR1 (100GE DAC)\n* `100gbase-cr2` - 100GBASE-CR2 (100GE DAC)\n* `100gbase-cr4` - 100GBASE-CR4 (100GE DAC)\n* `100gbase-cr10` - 100GBASE-CR10 (100GE DAC)\n* `100gbase-cwdm4` - 100GBASE-CWDM4 (100GE)\n* `100gbase-dr` - 100GBASE-DR (100GE)\n* `100gbase-er4` - 100GBASE-ER4 (100GE)\n* `100gbase-fr1` - 100GBASE-FR1 (100GE)\n* `100gbase-lr1` - 100GBASE-LR1 (100GE)\n* `100gbase-lr4` - 100GBASE-LR4 (100GE)\n* `100gbase-sr1` - 100GBASE-SR1 (100GE)\n* `100gbase-sr1.2` - 100GBASE-SR1.2 (100GE BiDi)\n* `100gbase-sr2` - 100GBASE-SR2 (100GE)\n* `100gbase-sr4` - 100GBASE-SR4 (100GE)\n* `100gbase-sr10` - 100GBASE-SR10 (100GE)\n* `100gbase-zr` - 100GBASE-ZR (100GE)\n* `200gbase-cr2` - 200GBASE-CR2 (200GE)\n* `200gbase-cr4` - 200GBASE-CR4 (200GE)\n* `200gbase-dr4` - 200GBASE-DR4 (200GE)\n* `200gbase-er4` - 200GBASE-ER4 (200GE)\n* `200gbase-fr4` - 200GBASE-FR4 (200GE)\n* `200gbase-lr4` - 200GBASE-LR4 (200GE)\n* `200gbase-sr2` - 200GBASE-SR2 (200GE)\n* `200gbase-sr4` - 200GBASE-SR4 (200GE)\n* `200gbase-vr2` - 200GBASE-VR2 (200GE)\n* `400gbase-cr4` - 400GBASE-CR4 (400GE)\n* `400gbase-dr4` - 400GBASE-DR4 (400GE)\n* `400gbase-er8` - 400GBASE-ER8 (400GE)\n* `400gbase-fr4` - 400GBASE-FR4 (400GE)\n* `400gbase-fr8` - 400GBASE-FR8 (400GE)\n* `400gbase-lr4` - 400GBASE-LR4 (400GE)\n* `400gbase-lr8` - 400GBASE-LR8 (400GE)\n* `400gbase-sr4` - 400GBASE-SR4 (400GE)\n* `400gbase-sr4_2` - 400GBASE-SR4.2 (400GE BiDi)\n* `400gbase-sr8` - 400GBASE-SR8 (400GE)\n* `400gbase-sr16` - 400GBASE-SR16 (400GE)\n* `400gbase-vr4` - 400GBASE-VR4 (400GE)\n* `400gbase-zr` - 400GBASE-ZR (400GE)\n* `800gbase-cr8` - 800GBASE-CR8 (800GE)\n* `800gbase-dr8` - 800GBASE-DR8 (800GE)\n* `800gbase-sr8` - 800GBASE-SR8 (800GE)\n* `800gbase-vr8` - 800GBASE-VR8 (800GE)\n* `1.6tbase-cr8` - 1.6TBASE-CR8 (1.6TE)\n* `1.6tbase-dr8` - 1.6TBASE-DR8 (1.6TE)\n* `1.6tbase-dr8-2` - 1.6TBASE-DR8-2 (1.6TE)\n* `100base-x-sfp` - SFP (100ME)\n* `1000base-x-gbic` - GBIC (1GE)\n* `1000base-x-sfp` - SFP (1GE)\n* `2.5gbase-x-sfp` - SFP (2.5GE)\n* `10gbase-x-sfpp` - SFP+ (10GE)\n* `10gbase-x-xenpak` - XENPAK (10GE)\n* `10gbase-x-xfp` - XFP (10GE)\n* `10gbase-x-x2` - X2 (10GE)\n* `25gbase-x-sfp28` - SFP28 (25GE)\n* `40gbase-x-qsfpp` - QSFP+ (40GE)\n* `50gbase-x-sfp28` - QSFP28 (50GE)\n* `50gbase-x-sfp56` - SFP56 (50GE)\n* `100gbase-x-cfp` - CFP (100GE)\n* `100gbase-x-cfp2` - CFP2 (100GE)\n* `100gbase-x-cfp4` - CFP4 (100GE)\n* `100gbase-x-cxp` - CXP (100GE)\n* `100gbase-x-cpak` - Cisco CPAK (100GE)\n* `100gbase-x-dsfp` - DSFP (100GE)\n* `100gbase-x-qsfp28` - QSFP28 (100GE)\n* `100gbase-x-qsfpdd` - QSFP-DD (100GE)\n* `100gbase-x-sfpdd` - SFP-DD (100GE)\n* `200gbase-x-cfp2` - CFP2 (200GE)\n* `200gbase-x-qsfp56` - QSFP56 (200GE)\n* `200gbase-x-qsfpdd` - QSFP-DD (200GE)\n* `400gbase-x-qsfp112` - QSFP112 (400GE)\n* `400gbase-x-qsfpdd` - QSFP-DD (400GE)\n* `400gbase-x-cdfp` - CDFP (400GE)\n* `400gbase-x-cfp2` - CFP2 (400GE)\n* `400gbase-x-cfp8` - CPF8 (400GE)\n* `400gbase-x-osfp` - OSFP (400GE)\n* `400gbase-x-osfp-rhs` - OSFP-RHS (400GE)\n* `800gbase-x-osfp` - OSFP (800GE)\n* `800gbase-x-qsfpdd` - QSFP-DD (800GE)\n* `1.6tbase-x-osfp1600` - OSFP1600 (1.6TE)\n* `1.6tbase-x-osfp1600-rhs` - OSFP1600-RHS (1.6TE)\n* `1.6tbase-x-qsfpdd1600` - QSFP-DD1600 (1.6TE)\n* `1000base-kx` - 1000BASE-KX (1GE)\n* `2.5gbase-kx` - 2.5GBASE-KX (2.5GE)\n* `5gbase-kr` - 5GBASE-KR (5GE)\n* `10gbase-kr` - 10GBASE-KR (10GE)\n* `10gbase-kx4` - 10GBASE-KX4 (10GE)\n* `25gbase-kr` - 25GBASE-KR (25GE)\n* `40gbase-kr4` - 40GBASE-KR4 (40GE)\n* `50gbase-kr` - 50GBASE-KR (50GE)\n* `100gbase-kp4` - 100GBASE-KP4 (100GE)\n* `100gbase-kr2` - 100GBASE-KR2 (100GE)\n* `100gbase-kr4` - 100GBASE-KR4 (100GE)\n* `1.6tbase-kr8` - 1.6TBASE-KR8 (1.6TE)\n* `ieee802.11a` - IEEE 802.11a\n* `ieee802.11g` - IEEE 802.11b/g\n* `ieee802.11n` - IEEE 802.11n (Wi-Fi 4)\n* `ieee802.11ac` - IEEE 802.11ac (Wi-Fi 5)\n* `ieee802.11ad` - IEEE 802.11ad (WiGig)\n* `ieee802.11ax` - IEEE 802.11ax (Wi-Fi 6)\n* `ieee802.11ay` - IEEE 802.11ay (WiGig)\n* `ieee802.11be` - IEEE 802.11be (Wi-Fi 7)\n* `ieee802.15.1` - IEEE 802.15.1 (Bluetooth)\n* `ieee802.15.4` - IEEE 802.15.4 (LR-WPAN)\n* `other-wireless` - Other (Wireless)\n* `gsm` - GSM\n* `cdma` - CDMA\n* `lte` - LTE\n* `4g` - 4G\n* `5g` - 5G\n* `sonet-oc3` - OC-3/STM-1\n* `sonet-oc12` - OC-12/STM-4\n* `sonet-oc48` - OC-48/STM-16\n* `sonet-oc192` - OC-192/STM-64\n* `sonet-oc768` - OC-768/STM-256\n* `sonet-oc1920` - OC-1920/STM-640\n* `sonet-oc3840` - OC-3840/STM-1234\n* `1gfc-sfp` - SFP (1GFC)\n* `2gfc-sfp` - SFP (2GFC)\n* `4gfc-sfp` - SFP (4GFC)\n* `8gfc-sfpp` - SFP+ (8GFC)\n* `16gfc-sfpp` - SFP+ (16GFC)\n* `32gfc-sfp28` - SFP28 (32GFC)\n* `32gfc-sfpp` - SFP+ (32GFC)\n* `64gfc-qsfpp` - QSFP+ (64GFC)\n* `64gfc-sfpdd` - SFP-DD (64GFC)\n* `64gfc-sfpp` - SFP+ (64GFC)\n* `128gfc-qsfp28` - QSFP28 (128GFC)\n* `infiniband-sdr` - SDR (2 Gbps)\n* `infiniband-ddr` - DDR (4 Gbps)\n* `infiniband-qdr` - QDR (8 Gbps)\n* `infiniband-fdr10` - FDR10 (10 Gbps)\n* `infiniband-fdr` - FDR (13.5 Gbps)\n* `infiniband-edr` - EDR (25 Gbps)\n* `infiniband-hdr` - HDR (50 Gbps)\n* `infiniband-ndr` - NDR (100 Gbps)\n* `infiniband-xdr` - XDR (250 Gbps)\n* `t1` - T1 (1.544 Mbps)\n* `e1` - E1 (2.048 Mbps)\n* `t3` - T3 (45 Mbps)\n* `e3` - E3 (34 Mbps)\n* `xdsl` - xDSL\n* `docsis` - DOCSIS\n* `moca` - MoCA\n* `bpon` - BPON (622 Mbps / 155 Mbps)\n* `epon` - EPON (1 Gbps)\n* `10g-epon` - 10G-EPON (10 Gbps)\n* `gpon` - GPON (2.5 Gbps / 1.25 Gbps)\n* `xg-pon` - XG-PON (10 Gbps / 2.5 Gbps)\n* `xgs-pon` - XGS-PON (10 Gbps)\n* `ng-pon2` - NG-PON2 (TWDM-PON) (4x10 Gbps)\n* `25g-pon` - 25G-PON (25 Gbps)\n* `50g-pon` - 50G-PON (50 Gbps)\n* `cisco-stackwise` - Cisco StackWise\n* `cisco-stackwise-plus` - Cisco StackWise Plus\n* `cisco-flexstack` - Cisco FlexStack\n* `cisco-flexstack-plus` - Cisco FlexStack Plus\n* `cisco-stackwise-80` - Cisco StackWise-80\n* `cisco-stackwise-160` - Cisco StackWise-160\n* `cisco-stackwise-320` - Cisco StackWise-320\n* `cisco-stackwise-480` - Cisco StackWise-480\n* `cisco-stackwise-1t` - Cisco StackWise-1T\n* `juniper-vcp` - Juniper VCP\n* `extreme-summitstack` - Extreme SummitStack\n* `extreme-summitstack-128` - Extreme SummitStack-128\n* `extreme-summitstack-256` - Extreme SummitStack-256\n* `extreme-summitstack-512` - Extreme SummitStack-512\n* `other` - Other", + "x-spec-enum-id": "b067eb1f050c6ae9" + }, + "enabled": { + "type": "boolean" + }, + "mgmt_only": { + "type": "boolean", + "title": "Management only" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "bridge": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceTemplateRequest" + } + ], + "nullable": true + }, + "poe_mode": { + "enum": [ + "pd", + "pse", + "", + null + ], + "type": "string", + "description": "* `pd` - PD\n* `pse` - PSE", + "x-spec-enum-id": "2f2fe6dcdc7772bd", + "nullable": true + }, + "poe_type": { + "enum": [ + "type1-ieee802.3af", + "type2-ieee802.3at", + "type3-ieee802.3bt", + "type4-ieee802.3bt", + "passive-24v-2pair", + "passive-24v-4pair", + "passive-48v-2pair", + "passive-48v-4pair", + "", + null + ], + "type": "string", + "description": "* `type1-ieee802.3af` - 802.3af (Type 1)\n* `type2-ieee802.3at` - 802.3at (Type 2)\n* `type3-ieee802.3bt` - 802.3bt (Type 3)\n* `type4-ieee802.3bt` - 802.3bt (Type 4)\n* `passive-24v-2pair` - Passive 24V (2-pair)\n* `passive-24v-4pair` - Passive 24V (4-pair)\n* `passive-48v-2pair` - Passive 48V (2-pair)\n* `passive-48v-4pair` - Passive 48V (4-pair)", + "x-spec-enum-id": "5473d57885f237ab", + "nullable": true + }, + "rf_role": { + "enum": [ + "ap", + "station", + "", + null + ], + "type": "string", + "description": "* `ap` - Access point\n* `station` - Station", + "x-spec-enum-id": "d2772dbea88b0fb1", + "nullable": true + } + }, + "required": [ + "id", + "name", + "type" + ] + }, + "BulkInventoryItemRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "parent": { + "type": "integer", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "staged", + "failed", + "decommissioning" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "545817eb4c4f2ae4" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefInventoryItemRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "part_id": { + "type": "string", + "description": "Manufacturer-assigned part identifier", + "maxLength": 50 + }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this item", + "maxLength": 50 + }, + "discovered": { + "type": "boolean", + "description": "This item was automatically discovered" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "component_type": { + "type": "string", + "nullable": true + }, + "component_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkInventoryItemRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkInventoryItemTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ] + }, + "parent": { + "type": "integer", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefInventoryItemRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "part_id": { + "type": "string", + "description": "Manufacturer-assigned part identifier", + "maxLength": 50 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "component_type": { + "type": "string", + "nullable": true + }, + "component_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + } + }, + "required": [ + "device_type", + "id", + "name" + ] + }, + "BulkJournalEntryRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "assigned_object_type": { + "type": "string" + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "created_by": { + "type": "integer", + "nullable": true + }, + "kind": { + "enum": [ + "info", + "success", + "warning", + "danger" + ], + "type": "string", + "description": "* `info` - Info\n* `success` - Success\n* `warning` - Warning\n* `danger` - Danger", + "x-spec-enum-id": "6f65abe0aab2c78c" + }, + "comments": { + "type": "string", + "minLength": 1 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "assigned_object_id", + "assigned_object_type", + "comments", + "id" + ] + }, + "BulkL2VPNRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "identifier": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": -9223372036854775808, + "format": "int64", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "type": { + "enum": [ + "vpws", + "vpls", + "vxlan", + "vxlan-evpn", + "mpls-evpn", + "pbb-evpn", + "evpn-vpws", + "epl", + "evpl", + "ep-lan", + "evp-lan", + "ep-tree", + "evp-tree", + "spb" + ], + "type": "string", + "description": "* `vpws` - VPWS\n* `vpls` - VPLS\n* `vxlan` - VXLAN\n* `vxlan-evpn` - VXLAN-EVPN\n* `mpls-evpn` - MPLS EVPN\n* `pbb-evpn` - PBB EVPN\n* `evpn-vpws` - EVPN VPWS\n* `epl` - EPL\n* `evpl` - EVPL\n* `ep-lan` - Ethernet Private LAN\n* `evp-lan` - Ethernet Virtual Private LAN\n* `ep-tree` - Ethernet Private Tree\n* `evp-tree` - Ethernet Virtual Private Tree\n* `spb` - SPB", + "x-spec-enum-id": "0a46f8056d717efc" + }, + "status": { + "enum": [ + "active", + "planned", + "decommissioning" + ], + "type": "string", + "description": "* `active` - Active\n* `planned` - Planned\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "8b9dc8efc7c3d5b0" + }, + "import_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "export_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkL2VPNTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "l2vpn": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefL2VPNRequest" + } + ] + }, + "assigned_object_type": { + "type": "string" + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "assigned_object_id", + "assigned_object_type", + "id", + "l2vpn" + ] + }, + "BulkLocationRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedLocationRequest" + } + ], + "nullable": true + }, + "status": { + "enum": [ + "planned", + "staging", + "active", + "decommissioning", + "retired" + ], + "type": "string", + "description": "* `planned` - Planned\n* `staging` - Staging\n* `active` - Active\n* `decommissioning` - Decommissioning\n* `retired` - Retired", + "x-spec-enum-id": "1cf60831fbb35e7f" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "facility": { + "type": "string", + "description": "Local facility ID or description", + "maxLength": 50 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "site", + "slug" + ] + }, + "BulkMACAddressRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "mac_address": { + "type": "string", + "minLength": 1 + }, + "assigned_object_type": { + "type": "string", + "nullable": true + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "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": [ + "id", + "mac_address" + ] + }, + "BulkManufacturerRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkModuleBayRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkModuleBayTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "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 + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkModuleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module_bay": { + "$ref": "#/components/schemas/NestedModuleBayRequest" + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ] + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "staged", + "failed", + "decommissioning" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "545817eb4c4f2ae4" + }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this device", + "maxLength": 50 + }, + "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": {} + }, + "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": [ + "device", + "id", + "module_bay", + "module_type" + ] + }, + "BulkModuleTypeProfileRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "schema": { + "nullable": true + }, + "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": [ + "id", + "name" + ] + }, + "BulkModuleTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "profile": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeProfileRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ] + }, + "model": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "part_number": { + "type": "string", + "description": "Discrete part number (optional)", + "maxLength": 50 + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "passive", + "", + null + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front\n* `left-to-right` - Left to right\n* `right-to-left` - Right to left\n* `side-to-rear` - Side to rear\n* `passive` - Passive", + "x-spec-enum-id": "5ad4e700c656b09d", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "attributes": { + "nullable": true + }, + "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": [ + "id", + "manufacturer", + "model" + ] + }, + "BulkNotificationGroupRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkNotificationRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + }, + "read": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "event_type": { + "enum": [ + "object_created", + "object_updated", + "object_deleted", + "job_started", + "job_completed", + "job_failed", + "job_errored" + ], + "type": "string", + "description": "* `object_created` - Object created\n* `object_updated` - Object updated\n* `object_deleted` - Object deleted\n* `job_started` - Job started\n* `job_completed` - Job completed\n* `job_failed` - Job failed\n* `job_errored` - Job errored", + "x-spec-enum-id": "01e557313a5c7bd2", + "title": "Event" + } + }, + "required": [ + "event_type", + "id", + "object_id", + "object_type", + "user" + ] + }, + "BulkObjectPermissionRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "enabled": { + "type": "boolean" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "actions": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 30 + }, + "description": "The list of actions granted by this permission" + }, + "constraints": { + "nullable": true, + "description": "Queryset filter matching the applicable objects of the selected type(s)" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "actions", + "id", + "name", + "object_types" + ] + }, + "BulkOwnerGroupRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkOwnerRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "user_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "group", + "id", + "name" + ] + }, + "BulkPlatformRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedPlatformRequest" + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "config_template": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkPowerFeedRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "power_panel": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefPowerPanelRequest" + } + ] + }, + "rack": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "failed" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `failed` - Failed", + "x-spec-enum-id": "ec530572dc778583" + }, + "type": { + "enum": [ + "primary", + "redundant" + ], + "type": "string", + "description": "* `primary` - Primary\n* `redundant` - Redundant", + "x-spec-enum-id": "093a164236819eb8" + }, + "supply": { + "enum": [ + "ac", + "dc" + ], + "type": "string", + "description": "* `ac` - AC\n* `dc` - DC", + "x-spec-enum-id": "1b6d99616ca6412b" + }, + "phase": { + "enum": [ + "single-phase", + "three-phase" + ], + "type": "string", + "description": "* `single-phase` - Single phase\n* `three-phase` - Three-phase", + "x-spec-enum-id": "994bc0696f4df57f" + }, + "voltage": { + "type": "integer", + "maximum": 32767, + "minimum": -32768 + }, + "amperage": { + "type": "integer", + "maximum": 32767, + "minimum": 1 + }, + "max_utilization": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "description": "Maximum permissible draw (percentage)" + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "power_panel" + ] + }, + "BulkPowerOutletRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c17", + "iec-60320-c19", + "iec-60320-c21", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15r", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-10-30r", + "nema-10-50r", + "nema-14-20r", + "nema-14-30r", + "nema-14-50r", + "nema-14-60r", + "nema-15-15r", + "nema-15-20r", + "nema-15-30r", + "nema-15-50r", + "nema-15-60r", + "nema-l1-15r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-15r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "nema-l10-30r", + "nema-l14-20r", + "nema-l14-30r", + "nema-l14-50r", + "nema-l14-60r", + "nema-l15-20r", + "nema-l15-30r", + "nema-l15-50r", + "nema-l15-60r", + "nema-l21-20r", + "nema-l21-30r", + "nema-l22-20r", + "nema-l22-30r", + "CS6360C", + "CS6364C", + "CS8164C", + "CS8264C", + "CS8364C", + "CS8464C", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "ita-multistandard", + "usb-a", + "usb-micro-b", + "usb-c", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "eaton-c39", + "hdot-cx", + "saf-d-grid", + "neutrik-powercon-20a", + "neutrik-powercon-32a", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c5` - C5\n* `iec-60320-c7` - C7\n* `iec-60320-c13` - C13\n* `iec-60320-c15` - C15\n* `iec-60320-c17` - C17\n* `iec-60320-c19` - C19\n* `iec-60320-c21` - C21\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15r` - NEMA 1-15R\n* `nema-5-15r` - NEMA 5-15R\n* `nema-5-20r` - NEMA 5-20R\n* `nema-5-30r` - NEMA 5-30R\n* `nema-5-50r` - NEMA 5-50R\n* `nema-6-15r` - NEMA 6-15R\n* `nema-6-20r` - NEMA 6-20R\n* `nema-6-30r` - NEMA 6-30R\n* `nema-6-50r` - NEMA 6-50R\n* `nema-10-30r` - NEMA 10-30R\n* `nema-10-50r` - NEMA 10-50R\n* `nema-14-20r` - NEMA 14-20R\n* `nema-14-30r` - NEMA 14-30R\n* `nema-14-50r` - NEMA 14-50R\n* `nema-14-60r` - NEMA 14-60R\n* `nema-15-15r` - NEMA 15-15R\n* `nema-15-20r` - NEMA 15-20R\n* `nema-15-30r` - NEMA 15-30R\n* `nema-15-50r` - NEMA 15-50R\n* `nema-15-60r` - NEMA 15-60R\n* `nema-l1-15r` - NEMA L1-15R\n* `nema-l5-15r` - NEMA L5-15R\n* `nema-l5-20r` - NEMA L5-20R\n* `nema-l5-30r` - NEMA L5-30R\n* `nema-l5-50r` - NEMA L5-50R\n* `nema-l6-15r` - NEMA L6-15R\n* `nema-l6-20r` - NEMA L6-20R\n* `nema-l6-30r` - NEMA L6-30R\n* `nema-l6-50r` - NEMA L6-50R\n* `nema-l10-30r` - NEMA L10-30R\n* `nema-l14-20r` - NEMA L14-20R\n* `nema-l14-30r` - NEMA L14-30R\n* `nema-l14-50r` - NEMA L14-50R\n* `nema-l14-60r` - NEMA L14-60R\n* `nema-l15-20r` - NEMA L15-20R\n* `nema-l15-30r` - NEMA L15-30R\n* `nema-l15-50r` - NEMA L15-50R\n* `nema-l15-60r` - NEMA L15-60R\n* `nema-l21-20r` - NEMA L21-20R\n* `nema-l21-30r` - NEMA L21-30R\n* `nema-l22-20r` - NEMA L22-20R\n* `nema-l22-30r` - NEMA L22-30R\n* `CS6360C` - CS6360C\n* `CS6364C` - CS6364C\n* `CS8164C` - CS8164C\n* `CS8264C` - CS8264C\n* `CS8364C` - CS8364C\n* `CS8464C` - CS8464C\n* `ita-e` - ITA Type E (CEE 7/5)\n* `ita-f` - ITA Type F (CEE 7/3)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `ita-multistandard` - ITA Multistandard\n* `usb-a` - USB Type A\n* `usb-micro-b` - USB Micro B\n* `usb-c` - USB Type C\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `eaton-c39` - Eaton C39\n* `hdot-cx` - HDOT Cx\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20a` - Neutrik powerCON (20A)\n* `neutrik-powercon-32a` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "db3e4eb2b93615f8", + "nullable": true + }, + "status": { + "enum": [ + "enabled", + "disabled", + "faulty" + ], + "type": "string", + "description": "* `enabled` - Enabled\n* `disabled` - Disabled\n* `faulty` - Faulty", + "x-spec-enum-id": "d60dce16858f3c69" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "power_port": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPowerPortRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "feed_leg": { + "enum": [ + "A", + "B", + "C", + "", + null + ], + "type": "string", + "description": "* `A` - A\n* `B` - B\n* `C` - C", + "x-spec-enum-id": "a4902339df0b7c06", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkPowerOutletTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c17", + "iec-60320-c19", + "iec-60320-c21", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15r", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-10-30r", + "nema-10-50r", + "nema-14-20r", + "nema-14-30r", + "nema-14-50r", + "nema-14-60r", + "nema-15-15r", + "nema-15-20r", + "nema-15-30r", + "nema-15-50r", + "nema-15-60r", + "nema-l1-15r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-15r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "nema-l10-30r", + "nema-l14-20r", + "nema-l14-30r", + "nema-l14-50r", + "nema-l14-60r", + "nema-l15-20r", + "nema-l15-30r", + "nema-l15-50r", + "nema-l15-60r", + "nema-l21-20r", + "nema-l21-30r", + "nema-l22-20r", + "nema-l22-30r", + "CS6360C", + "CS6364C", + "CS8164C", + "CS8264C", + "CS8364C", + "CS8464C", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "ita-multistandard", + "usb-a", + "usb-micro-b", + "usb-c", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "eaton-c39", + "hdot-cx", + "saf-d-grid", + "neutrik-powercon-20a", + "neutrik-powercon-32a", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c5` - C5\n* `iec-60320-c7` - C7\n* `iec-60320-c13` - C13\n* `iec-60320-c15` - C15\n* `iec-60320-c17` - C17\n* `iec-60320-c19` - C19\n* `iec-60320-c21` - C21\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15r` - NEMA 1-15R\n* `nema-5-15r` - NEMA 5-15R\n* `nema-5-20r` - NEMA 5-20R\n* `nema-5-30r` - NEMA 5-30R\n* `nema-5-50r` - NEMA 5-50R\n* `nema-6-15r` - NEMA 6-15R\n* `nema-6-20r` - NEMA 6-20R\n* `nema-6-30r` - NEMA 6-30R\n* `nema-6-50r` - NEMA 6-50R\n* `nema-10-30r` - NEMA 10-30R\n* `nema-10-50r` - NEMA 10-50R\n* `nema-14-20r` - NEMA 14-20R\n* `nema-14-30r` - NEMA 14-30R\n* `nema-14-50r` - NEMA 14-50R\n* `nema-14-60r` - NEMA 14-60R\n* `nema-15-15r` - NEMA 15-15R\n* `nema-15-20r` - NEMA 15-20R\n* `nema-15-30r` - NEMA 15-30R\n* `nema-15-50r` - NEMA 15-50R\n* `nema-15-60r` - NEMA 15-60R\n* `nema-l1-15r` - NEMA L1-15R\n* `nema-l5-15r` - NEMA L5-15R\n* `nema-l5-20r` - NEMA L5-20R\n* `nema-l5-30r` - NEMA L5-30R\n* `nema-l5-50r` - NEMA L5-50R\n* `nema-l6-15r` - NEMA L6-15R\n* `nema-l6-20r` - NEMA L6-20R\n* `nema-l6-30r` - NEMA L6-30R\n* `nema-l6-50r` - NEMA L6-50R\n* `nema-l10-30r` - NEMA L10-30R\n* `nema-l14-20r` - NEMA L14-20R\n* `nema-l14-30r` - NEMA L14-30R\n* `nema-l14-50r` - NEMA L14-50R\n* `nema-l14-60r` - NEMA L14-60R\n* `nema-l15-20r` - NEMA L15-20R\n* `nema-l15-30r` - NEMA L15-30R\n* `nema-l15-50r` - NEMA L15-50R\n* `nema-l15-60r` - NEMA L15-60R\n* `nema-l21-20r` - NEMA L21-20R\n* `nema-l21-30r` - NEMA L21-30R\n* `nema-l22-20r` - NEMA L22-20R\n* `nema-l22-30r` - NEMA L22-30R\n* `CS6360C` - CS6360C\n* `CS6364C` - CS6364C\n* `CS8164C` - CS8164C\n* `CS8264C` - CS8264C\n* `CS8364C` - CS8364C\n* `CS8464C` - CS8464C\n* `ita-e` - ITA Type E (CEE 7/5)\n* `ita-f` - ITA Type F (CEE 7/3)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `ita-multistandard` - ITA Multistandard\n* `usb-a` - USB Type A\n* `usb-micro-b` - USB Micro B\n* `usb-c` - USB Type C\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `eaton-c39` - Eaton C39\n* `hdot-cx` - HDOT Cx\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20a` - Neutrik powerCON (20A)\n* `neutrik-powercon-32a` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "db3e4eb2b93615f8", + "nullable": true + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "power_port": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPowerPortTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "feed_leg": { + "enum": [ + "A", + "B", + "C", + "", + null + ], + "type": "string", + "description": "* `A` - A\n* `B` - B\n* `C` - C", + "x-spec-enum-id": "a4902339df0b7c06", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkPowerPanelRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "location": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefLocationRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "site" + ] + }, + "BulkPowerPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c18", + "iec-60320-c20", + "iec-60320-c22", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15p", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-10-30p", + "nema-10-50p", + "nema-14-20p", + "nema-14-30p", + "nema-14-50p", + "nema-14-60p", + "nema-15-15p", + "nema-15-20p", + "nema-15-30p", + "nema-15-50p", + "nema-15-60p", + "nema-l1-15p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-15p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "nema-l10-30p", + "nema-l14-20p", + "nema-l14-30p", + "nema-l14-50p", + "nema-l14-60p", + "nema-l15-20p", + "nema-l15-30p", + "nema-l15-50p", + "nema-l15-60p", + "nema-l21-20p", + "nema-l21-30p", + "nema-l22-20p", + "nema-l22-30p", + "cs6361c", + "cs6365c", + "cs8165c", + "cs8265c", + "cs8365c", + "cs8465c", + "ita-c", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "usb-3-b", + "usb-3-micro-b", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "saf-d-grid", + "neutrik-powercon-20", + "neutrik-powercon-32", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c6` - C6\n* `iec-60320-c8` - C8\n* `iec-60320-c14` - C14\n* `iec-60320-c16` - C16\n* `iec-60320-c18` - C18\n* `iec-60320-c20` - C20\n* `iec-60320-c22` - C22\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15p` - NEMA 1-15P\n* `nema-5-15p` - NEMA 5-15P\n* `nema-5-20p` - NEMA 5-20P\n* `nema-5-30p` - NEMA 5-30P\n* `nema-5-50p` - NEMA 5-50P\n* `nema-6-15p` - NEMA 6-15P\n* `nema-6-20p` - NEMA 6-20P\n* `nema-6-30p` - NEMA 6-30P\n* `nema-6-50p` - NEMA 6-50P\n* `nema-10-30p` - NEMA 10-30P\n* `nema-10-50p` - NEMA 10-50P\n* `nema-14-20p` - NEMA 14-20P\n* `nema-14-30p` - NEMA 14-30P\n* `nema-14-50p` - NEMA 14-50P\n* `nema-14-60p` - NEMA 14-60P\n* `nema-15-15p` - NEMA 15-15P\n* `nema-15-20p` - NEMA 15-20P\n* `nema-15-30p` - NEMA 15-30P\n* `nema-15-50p` - NEMA 15-50P\n* `nema-15-60p` - NEMA 15-60P\n* `nema-l1-15p` - NEMA L1-15P\n* `nema-l5-15p` - NEMA L5-15P\n* `nema-l5-20p` - NEMA L5-20P\n* `nema-l5-30p` - NEMA L5-30P\n* `nema-l5-50p` - NEMA L5-50P\n* `nema-l6-15p` - NEMA L6-15P\n* `nema-l6-20p` - NEMA L6-20P\n* `nema-l6-30p` - NEMA L6-30P\n* `nema-l6-50p` - NEMA L6-50P\n* `nema-l10-30p` - NEMA L10-30P\n* `nema-l14-20p` - NEMA L14-20P\n* `nema-l14-30p` - NEMA L14-30P\n* `nema-l14-50p` - NEMA L14-50P\n* `nema-l14-60p` - NEMA L14-60P\n* `nema-l15-20p` - NEMA L15-20P\n* `nema-l15-30p` - NEMA L15-30P\n* `nema-l15-50p` - NEMA L15-50P\n* `nema-l15-60p` - NEMA L15-60P\n* `nema-l21-20p` - NEMA L21-20P\n* `nema-l21-30p` - NEMA L21-30P\n* `nema-l22-20p` - NEMA L22-20P\n* `nema-l22-30p` - NEMA L22-30P\n* `cs6361c` - CS6361C\n* `cs6365c` - CS6365C\n* `cs8165c` - CS8165C\n* `cs8265c` - CS8265C\n* `cs8365c` - CS8365C\n* `cs8465c` - CS8465C\n* `ita-c` - ITA Type C (CEE 7/16)\n* `ita-e` - ITA Type E (CEE 7/6)\n* `ita-f` - ITA Type F (CEE 7/4)\n* `ita-ef` - ITA Type E/F (CEE 7/7)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `usb-3-b` - USB 3.0 Type B\n* `usb-3-micro-b` - USB 3.0 Micro B\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20` - Neutrik powerCON (20A)\n* `neutrik-powercon-32` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "aadcbe6ca854c1ed", + "nullable": true + }, + "maximum_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Maximum power draw (watts)" + }, + "allocated_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Allocated power draw (watts)" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name" + ] + }, + "BulkPowerPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c18", + "iec-60320-c20", + "iec-60320-c22", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15p", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-10-30p", + "nema-10-50p", + "nema-14-20p", + "nema-14-30p", + "nema-14-50p", + "nema-14-60p", + "nema-15-15p", + "nema-15-20p", + "nema-15-30p", + "nema-15-50p", + "nema-15-60p", + "nema-l1-15p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-15p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "nema-l10-30p", + "nema-l14-20p", + "nema-l14-30p", + "nema-l14-50p", + "nema-l14-60p", + "nema-l15-20p", + "nema-l15-30p", + "nema-l15-50p", + "nema-l15-60p", + "nema-l21-20p", + "nema-l21-30p", + "nema-l22-20p", + "nema-l22-30p", + "cs6361c", + "cs6365c", + "cs8165c", + "cs8265c", + "cs8365c", + "cs8465c", + "ita-c", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "usb-3-b", + "usb-3-micro-b", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "saf-d-grid", + "neutrik-powercon-20", + "neutrik-powercon-32", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c6` - C6\n* `iec-60320-c8` - C8\n* `iec-60320-c14` - C14\n* `iec-60320-c16` - C16\n* `iec-60320-c18` - C18\n* `iec-60320-c20` - C20\n* `iec-60320-c22` - C22\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15p` - NEMA 1-15P\n* `nema-5-15p` - NEMA 5-15P\n* `nema-5-20p` - NEMA 5-20P\n* `nema-5-30p` - NEMA 5-30P\n* `nema-5-50p` - NEMA 5-50P\n* `nema-6-15p` - NEMA 6-15P\n* `nema-6-20p` - NEMA 6-20P\n* `nema-6-30p` - NEMA 6-30P\n* `nema-6-50p` - NEMA 6-50P\n* `nema-10-30p` - NEMA 10-30P\n* `nema-10-50p` - NEMA 10-50P\n* `nema-14-20p` - NEMA 14-20P\n* `nema-14-30p` - NEMA 14-30P\n* `nema-14-50p` - NEMA 14-50P\n* `nema-14-60p` - NEMA 14-60P\n* `nema-15-15p` - NEMA 15-15P\n* `nema-15-20p` - NEMA 15-20P\n* `nema-15-30p` - NEMA 15-30P\n* `nema-15-50p` - NEMA 15-50P\n* `nema-15-60p` - NEMA 15-60P\n* `nema-l1-15p` - NEMA L1-15P\n* `nema-l5-15p` - NEMA L5-15P\n* `nema-l5-20p` - NEMA L5-20P\n* `nema-l5-30p` - NEMA L5-30P\n* `nema-l5-50p` - NEMA L5-50P\n* `nema-l6-15p` - NEMA L6-15P\n* `nema-l6-20p` - NEMA L6-20P\n* `nema-l6-30p` - NEMA L6-30P\n* `nema-l6-50p` - NEMA L6-50P\n* `nema-l10-30p` - NEMA L10-30P\n* `nema-l14-20p` - NEMA L14-20P\n* `nema-l14-30p` - NEMA L14-30P\n* `nema-l14-50p` - NEMA L14-50P\n* `nema-l14-60p` - NEMA L14-60P\n* `nema-l15-20p` - NEMA L15-20P\n* `nema-l15-30p` - NEMA L15-30P\n* `nema-l15-50p` - NEMA L15-50P\n* `nema-l15-60p` - NEMA L15-60P\n* `nema-l21-20p` - NEMA L21-20P\n* `nema-l21-30p` - NEMA L21-30P\n* `nema-l22-20p` - NEMA L22-20P\n* `nema-l22-30p` - NEMA L22-30P\n* `cs6361c` - CS6361C\n* `cs6365c` - CS6365C\n* `cs8165c` - CS8165C\n* `cs8265c` - CS8265C\n* `cs8365c` - CS8365C\n* `cs8465c` - CS8465C\n* `ita-c` - ITA Type C (CEE 7/16)\n* `ita-e` - ITA Type E (CEE 7/6)\n* `ita-f` - ITA Type F (CEE 7/4)\n* `ita-ef` - ITA Type E/F (CEE 7/7)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `usb-3-b` - USB 3.0 Type B\n* `usb-3-micro-b` - USB 3.0 Micro B\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20` - Neutrik powerCON (20A)\n* `neutrik-powercon-32` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "aadcbe6ca854c1ed", + "nullable": true + }, + "maximum_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Maximum power draw (watts)" + }, + "allocated_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Allocated power draw (watts)" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkPrefixRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "prefix": { + "type": "string", + "minLength": 1 + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "container", + "active", + "reserved", + "deprecated" + ], + "type": "string", + "description": "* `container` - Container\n* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated", + "x-spec-enum-id": "026173ce39f2ee63" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "is_pool": { + "type": "boolean", + "title": "Is a pool", + "description": "All IP addresses within this prefix are considered usable" + }, + "mark_utilized": { + "type": "boolean", + "description": "Treat as fully utilized" + }, + "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": [ + "id", + "prefix" + ] + }, + "BulkProviderAccountRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "provider": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderRequest" + } + ] + }, + "name": { + "type": "string", + "default": "", + "maxLength": 100 + }, + "account": { + "type": "string", + "minLength": 1, + "title": "Account ID", + "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": [ + "account", + "id", + "provider" + ] + }, + "BulkProviderNetworkRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "provider": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "service_id": { + "type": "string", + "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": [ + "id", + "name", + "provider" + ] + }, + "BulkProviderRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Full name of the provider", + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "accounts": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "asns": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkRIRRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "is_private": { + "type": "boolean", + "title": "Private", + "description": "IP space managed by this RIR is considered private" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkRackGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkRackRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "facility_id": { + "type": "string", + "nullable": true, + "maxLength": 50 + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "location": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefLocationRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "reserved", + "available", + "planned", + "active", + "deprecated" + ], + "type": "string", + "description": "* `reserved` - Reserved\n* `available` - Available\n* `planned` - Planned\n* `active` - Active\n* `deprecated` - Deprecated", + "x-spec-enum-id": "76eea4eef8804bcb" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this rack", + "maxLength": 50 + }, + "rack_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "form_factor": { + "enum": [ + "2-post-frame", + "4-post-frame", + "4-post-cabinet", + "wall-frame", + "wall-frame-vertical", + "wall-cabinet", + "wall-cabinet-vertical", + "", + null + ], + "type": "string", + "description": "* `2-post-frame` - 2-post frame\n* `4-post-frame` - 4-post frame\n* `4-post-cabinet` - 4-post cabinet\n* `wall-frame` - Wall-mounted frame\n* `wall-frame-vertical` - Wall-mounted frame (vertical)\n* `wall-cabinet` - Wall-mounted cabinet\n* `wall-cabinet-vertical` - Wall-mounted cabinet (vertical)", + "x-spec-enum-id": "8a902fde21d48841", + "nullable": true + }, + "width": { + "enum": [ + 10, + 19, + 21, + 23 + ], + "type": "integer", + "description": "* `10` - 10 inches\n* `19` - 19 inches\n* `21` - 21 inches\n* `23` - 23 inches", + "x-spec-enum-id": "9b322795f297a9c3" + }, + "u_height": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "title": "Height (U)", + "description": "Height in rack units" + }, + "starting_unit": { + "type": "integer", + "maximum": 32767, + "minimum": 1, + "description": "Starting unit for rack" + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "max_weight": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "description": "Maximum load capacity for the rack" + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "desc_units": { + "type": "boolean", + "title": "Descending units", + "description": "Units are numbered top-to-bottom" + }, + "outer_width": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (width)" + }, + "outer_height": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (height)" + }, + "outer_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (depth)" + }, + "outer_unit": { + "enum": [ + "mm", + "in", + "", + null + ], + "type": "string", + "description": "* `mm` - Millimeters\n* `in` - Inches", + "x-spec-enum-id": "3d701848b66312c3", + "nullable": true + }, + "mounting_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails." + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "" + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front", + "x-spec-enum-id": "a784734d07ef1b3c" + }, + "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": [ + "id", + "name", + "site" + ] + }, + "BulkRackReservationRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "rack": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefRackRequest" + } + ] + }, + "units": { + "type": "array", + "items": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + } + }, + "status": { + "enum": [ + "pending", + "active", + "stale" + ], + "type": "string", + "description": "* `pending` - Pending\n* `active` - Active\n* `stale` - Stale", + "x-spec-enum-id": "ed6038a4deee151c" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "minLength": 1, + "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": [ + "description", + "id", + "rack", + "units", + "user" + ] + }, + "BulkRackRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkRackTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ] + }, + "model": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "form_factor": { + "enum": [ + "2-post-frame", + "4-post-frame", + "4-post-cabinet", + "wall-frame", + "wall-frame-vertical", + "wall-cabinet", + "wall-cabinet-vertical", + "", + null + ], + "type": "string", + "description": "* `2-post-frame` - 2-post frame\n* `4-post-frame` - 4-post frame\n* `4-post-cabinet` - 4-post cabinet\n* `wall-frame` - Wall-mounted frame\n* `wall-frame-vertical` - Wall-mounted frame (vertical)\n* `wall-cabinet` - Wall-mounted cabinet\n* `wall-cabinet-vertical` - Wall-mounted cabinet (vertical)", + "x-spec-enum-id": "8a902fde21d48841", + "nullable": true + }, + "width": { + "enum": [ + 10, + 19, + 21, + 23 + ], + "type": "integer", + "description": "* `10` - 10 inches\n* `19` - 19 inches\n* `21` - 21 inches\n* `23` - 23 inches", + "x-spec-enum-id": "9b322795f297a9c3" + }, + "u_height": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "title": "Height (U)", + "description": "Height in rack units" + }, + "starting_unit": { + "type": "integer", + "maximum": 32767, + "minimum": 1, + "description": "Starting unit for rack" + }, + "desc_units": { + "type": "boolean", + "title": "Descending units", + "description": "Units are numbered top-to-bottom" + }, + "outer_width": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (width)" + }, + "outer_height": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (height)" + }, + "outer_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (depth)" + }, + "outer_unit": { + "enum": [ + "mm", + "in", + "", + null + ], + "type": "string", + "description": "* `mm` - Millimeters\n* `in` - Inches", + "x-spec-enum-id": "3d701848b66312c3", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "max_weight": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "description": "Maximum load capacity for the rack" + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "mounting_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails." + }, + "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": [ + "id", + "manufacturer", + "model", + "slug" + ] + }, + "BulkRearPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "front_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RearPortMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "device", + "id", + "name", + "type" + ] + }, + "BulkRearPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "front_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RearPortTemplateMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "name", + "type" + ] + }, + "BulkRegionRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedRegionRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkRouteTargetRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Route target value (formatted in accordance with RFC 4360)", + "maxLength": 21 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name" + ] + }, + "BulkSavedFilterRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "user": { + "type": "integer", + "nullable": true + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "enabled": { + "type": "boolean" + }, + "shared": { + "type": "boolean" + }, + "parameters": {}, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id", + "name", + "object_types", + "parameters", + "slug" + ] + }, + "BulkServiceRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "parent_object_type": { + "type": "string" + }, + "parent_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "protocol": { + "enum": [ + "tcp", + "udp", + "sctp" + ], + "type": "string", + "description": "* `tcp` - TCP\n* `udp` - UDP\n* `sctp` - SCTP", + "x-spec-enum-id": "e4b15bec749a2a32" + }, + "ports": { + "type": "array", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 1 + }, + "title": "Port numbers" + }, + "ipaddresses": { + "type": "array", + "items": { + "type": "integer" + } + }, + "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": [ + "id", + "name", + "parent_object_id", + "parent_object_type", + "ports" + ] + }, + "BulkServiceTemplateRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "protocol": { + "enum": [ + "tcp", + "udp", + "sctp" + ], + "type": "string", + "description": "* `tcp` - TCP\n* `udp` - UDP\n* `sctp` - SCTP", + "x-spec-enum-id": "e4b15bec749a2a32" + }, + "ports": { + "type": "array", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 1 + }, + "title": "Port numbers" + }, + "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": [ + "id", + "name", + "ports" + ] + }, + "BulkSiteGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedSiteGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkSiteRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Full name of the site", + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "status": { + "enum": [ + "planned", + "staging", + "active", + "decommissioning", + "retired" + ], + "type": "string", + "description": "* `planned` - Planned\n* `staging` - Staging\n* `active` - Active\n* `decommissioning` - Decommissioning\n* `retired` - Retired", + "x-spec-enum-id": "1cf60831fbb35e7f" + }, + "region": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRegionRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefSiteGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "facility": { + "type": "string", + "description": "Local facility ID or description", + "maxLength": 50 + }, + "time_zone": { + "type": "string", + "nullable": true, + "minLength": 1 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "physical_address": { + "type": "string", + "description": "Physical location of the building", + "maxLength": 200 + }, + "shipping_address": { + "type": "string", + "description": "If different from the physical address", + "maxLength": 200 + }, + "latitude": { + "type": "number", + "format": "double", + "maximum": 90.0, + "minimum": -90.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "longitude": { + "type": "number", + "format": "double", + "maximum": 180.0, + "minimum": -180.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "asns": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkSubscriptionRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + } + }, + "required": [ + "id", + "object_id", + "object_type", + "user" + ] + }, + "BulkTableConfigRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "table": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "user": { + "type": "integer", + "nullable": true + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "enabled": { + "type": "boolean" + }, + "shared": { + "type": "boolean" + }, + "columns": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + }, + "ordering": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "nullable": true + } + }, + "required": [ + "columns", + "id", + "name", + "object_type", + "table" + ] + }, + "BulkTagRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "pattern": "^[-\\w]+$", + "maxLength": 100 + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkTenantGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedTenantGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkTenantRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkTokenRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "version": { + "enum": [ + 1, + 2 + ], + "type": "integer", + "description": "* `1` - v1\n* `2` - v2", + "x-spec-enum-id": "b5df70f0bffd12cb", + "minimum": 0, + "maximum": 32767 + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "expires": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_used": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "enabled": { + "type": "boolean", + "description": "Disable to temporarily revoke this token without deleting it." + }, + "write_enabled": { + "type": "boolean", + "description": "Permit create/update/delete operations using this token" + }, + "pepper_id": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "ID of the cryptographic pepper used to hash the token (v2 only)" + }, + "token": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id", + "user" + ] + }, + "BulkTunnelGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkTunnelRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "status": { + "enum": [ + "planned", + "active", + "disabled" + ], + "type": "string", + "description": "* `planned` - Planned\n* `active` - Active\n* `disabled` - Disabled", + "x-spec-enum-id": "2431ef62c418f485" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTunnelGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "encapsulation": { + "enum": [ + "ipsec-transport", + "ipsec-tunnel", + "ip-ip", + "gre", + "wireguard", + "openvpn", + "l2tp", + "pptp" + ], + "type": "string", + "description": "* `ipsec-transport` - IPsec - Transport\n* `ipsec-tunnel` - IPsec - Tunnel\n* `ip-ip` - IP-in-IP\n* `gre` - GRE\n* `wireguard` - WireGuard\n* `openvpn` - OpenVPN\n* `l2tp` - L2TP\n* `pptp` - PPTP", + "x-spec-enum-id": "4f3254459f0e94f0" + }, + "ipsec_profile": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPSecProfileRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tunnel_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "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": [ + "encapsulation", + "id", + "name", + "status" + ] + }, + "BulkTunnelTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "tunnel": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefTunnelRequest" + } + ] + }, + "role": { + "enum": [ + "peer", + "hub", + "spoke" + ], + "type": "string", + "description": "* `peer` - Peer\n* `hub` - Hub\n* `spoke` - Spoke", + "x-spec-enum-id": "0b3bfadcebd86b58" + }, + "termination_type": { + "type": "string" + }, + "termination_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "outside_ip": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "role", + "termination_type", + "tunnel" + ] + }, + "BulkUserRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "username": { + "type": "string", + "minLength": 1, + "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + "pattern": "^[\\w.@+-]+$", + "maxLength": 150 + }, + "password": { + "type": "string", + "writeOnly": true, + "minLength": 1, + "maxLength": 128 + }, + "first_name": { + "type": "string", + "maxLength": 150 + }, + "last_name": { + "type": "string", + "maxLength": 150 + }, + "email": { + "type": "string", + "format": "email", + "title": "Email address", + "maxLength": 254 + }, + "is_active": { + "type": "boolean", + "title": "Active", + "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." + }, + "date_joined": { + "type": "string", + "format": "date-time" + }, + "last_login": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "permissions": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id", + "password", + "username" + ] + }, + "BulkVLANGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "vid_ranges": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegerRangeRequest" + } + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkVLANRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vid": { + "type": "integer", + "maximum": 4094, + "minimum": 1, + "title": "VLAN ID", + "description": "Numeric VLAN ID (1-4094)" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "deprecated" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated", + "x-spec-enum-id": "ca933c38b935e547" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "qinq_role": { + "enum": [ + "svlan", + "cvlan", + null + ], + "type": "string", + "description": "* `svlan` - Service\n* `cvlan` - Customer", + "x-spec-enum-id": "fa0abd59fb1a7312", + "nullable": true + }, + "qinq_svlan": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedVLANRequest" + } + ], + "nullable": true + }, + "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": [ + "id", + "name", + "vid" + ] + }, + "BulkVLANTranslationPolicyRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "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" + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkVLANTranslationRuleRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "policy": { + "type": "integer" + }, + "local_vid": { + "type": "integer", + "maximum": 4094, + "minimum": 1, + "title": "Local VLAN ID", + "description": "Numeric VLAN ID (1-4094)" + }, + "remote_vid": { + "type": "integer", + "maximum": 4094, + "minimum": 1, + "title": "Remote VLAN ID", + "description": "Numeric VLAN ID (1-4094)" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id", + "local_vid", + "policy", + "remote_vid" + ] + }, + "BulkVMInterfaceRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "virtual_machine": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualMachineRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "enabled": { + "type": "boolean" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedVMInterfaceRequest" + } + ], + "nullable": true + }, + "bridge": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedVMInterfaceRequest" + } + ], + "nullable": true + }, + "mtu": { + "type": "integer", + "maximum": 65536, + "minimum": 1, + "nullable": true + }, + "primary_mac_address": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefMACAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mode": { + "enum": [ + "access", + "tagged", + "tagged-all", + "q-in-q", + "" + ], + "type": "string", + "description": "* `access` - Access\n* `tagged` - Tagged\n* `tagged-all` - Tagged (All)\n* `q-in-q` - Q-in-Q (802.1ad)", + "x-spec-enum-id": "84129b71b974ebe5" + }, + "untagged_vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tagged_vlans": { + "type": "array", + "items": { + "type": "integer" + } + }, + "qinq_svlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vlan_translation_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANTranslationPolicyRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name", + "virtual_machine" + ] + }, + "BulkVRFRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "rd": { + "type": "string", + "nullable": true, + "title": "Route distinguisher", + "description": "Unique route distinguisher (as defined in RFC 4364)", + "maxLength": 21 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "enforce_unique": { + "type": "boolean", + "title": "Enforce unique space", + "description": "Prevent duplicate prefixes/IP addresses within this VRF" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "import_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "export_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkVirtualChassisRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "domain": { + "type": "string", + "maxLength": 30 + }, + "master": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedDeviceRequest" + } + ], + "nullable": true + }, + "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": [ + "id", + "name" + ] + }, + "BulkVirtualCircuitRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "cid": { + "type": "string", + "minLength": 1, + "title": "Circuit ID", + "description": "Unique circuit ID", + "maxLength": 100 + }, + "provider_network": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderNetworkRequest" + } + ] + }, + "provider_account": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefProviderAccountRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualCircuitTypeRequest" + } + ] + }, + "status": { + "enum": [ + "planned", + "provisioning", + "active", + "offline", + "deprovisioning", + "decommissioned" + ], + "type": "string", + "description": "* `planned` - Planned\n* `provisioning` - Provisioning\n* `active` - Active\n* `offline` - Offline\n* `deprovisioning` - Deprovisioning\n* `decommissioned` - Decommissioned", + "x-spec-enum-id": "0a239d878b6666a4" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "cid", + "id", + "provider_network", + "type" + ] + }, + "BulkVirtualCircuitTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "virtual_circuit": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualCircuitRequest" + } + ] + }, + "role": { + "enum": [ + "peer", + "hub", + "spoke" + ], + "type": "string", + "description": "* `peer` - Peer\n* `hub` - Hub\n* `spoke` - Spoke", + "x-spec-enum-id": "0b3bfadcebd86b58" + }, + "interface": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefInterfaceRequest" + } + ] + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "interface", + "virtual_circuit" + ] + }, + "BulkVirtualCircuitTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkVirtualDeviceContextRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "identifier": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip4": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip6": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "planned", + "offline" + ], + "type": "string", + "description": "* `active` - Active\n* `planned` - Planned\n* `offline` - Offline", + "x-spec-enum-id": "0e2c0919d51b83cb" + }, + "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": [ + "device", + "id", + "name", + "status" + ] + }, + "BulkVirtualDiskRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "virtual_machine": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualMachineRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "size": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name", + "size", + "virtual_machine" + ] + }, + "BulkVirtualMachineTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id", + "name", + "slug" + ] + }, + "BulkVirtualMachineWithConfigContextRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "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", + "active", + "planned", + "staged", + "failed", + "decommissioning", + "paused" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `decommissioning` - Decommissioning\n* `paused` - Paused", + "x-spec-enum-id": "effecc3b94e0b74b" + }, + "start_on_boot": { + "enum": [ + "on", + "off", + "laststate" + ], + "type": "string", + "description": "* `on` - On\n* `off` - Off\n* `laststate` - Last State", + "x-spec-enum-id": "610e33fc2fde73d6" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "cluster": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefClusterRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip4": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip6": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vcpus": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": 0.01, + "exclusiveMaximum": true, + "nullable": true + }, + "memory": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "disk": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "description": { + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "name" + ] + }, + "BulkWebhookRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 150 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "payload_url": { + "type": "string", + "minLength": 1, + "title": "URL", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "maxLength": 500 + }, + "http_method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "type": "string", + "description": "* `GET` - GET\n* `POST` - POST\n* `PUT` - PUT\n* `PATCH` - PATCH\n* `DELETE` - DELETE", + "x-spec-enum-id": "867bf764d3b1eeaa" + }, + "http_content_type": { + "type": "string", + "minLength": 1, + "description": "The complete list of official content types is available here.", + "maxLength": 100 + }, + "additional_headers": { + "type": "string", + "description": "User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. Headers should be defined in the format Name: Value. Jinja2 template processing is supported with the same context as the request body (below)." + }, + "body_template": { + "type": "string", + "description": "Jinja2 template for a custom request body. If blank, a JSON object representing the change will be included. Available context data includes: event, model, timestamp, username, request_id, and data." + }, + "secret": { + "type": "string", + "description": "When provided, the request will include a X-Hook-Signature header containing a HMAC hex digest of the payload body using the secret as the key. The secret is not transmitted in the request.", + "maxLength": 255 + }, + "ssl_verification": { + "type": "boolean", + "description": "Enable SSL certificate verification. Disable with caution!" + }, + "ca_file_path": { + "type": "string", + "nullable": true, + "description": "The specific CA certificate file to use for SSL verification. Leave blank to use the system defaults.", + "maxLength": 4096 + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "id", + "name", + "payload_url" + ] + }, + "BulkWirelessLANGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedWirelessLANGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "BulkWirelessLANRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "ssid": { + "type": "string", + "minLength": 1, + "maxLength": 32 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefWirelessLANGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "disabled", + "deprecated", + "" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `disabled` - Disabled\n* `deprecated` - Deprecated", + "x-spec-enum-id": "e5549d7370ce2e6c" + }, + "vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "auth_type": { + "enum": [ + "open", + "wep", + "wpa-personal", + "wpa-enterprise", + "" + ], + "type": "string", + "description": "* `open` - Open\n* `wep` - WEP\n* `wpa-personal` - WPA Personal (PSK)\n* `wpa-enterprise` - WPA Enterprise", + "x-spec-enum-id": "e917c12aac765910" + }, + "auth_cipher": { + "enum": [ + "auto", + "tkip", + "aes", + "" + ], + "type": "string", + "description": "* `auto` - Auto\n* `tkip` - TKIP\n* `aes` - AES", + "x-spec-enum-id": "42f867e89988bb0c" + }, + "auth_psk": { + "type": "string", + "title": "Pre-shared key", + "maxLength": 64 + }, + "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": [ + "id", + "ssid" + ] + }, + "BulkWirelessLinkRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "interface_a": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefInterfaceRequest" + } + ] + }, + "interface_b": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefInterfaceRequest" + } + ] + }, + "ssid": { + "type": "string", + "maxLength": 32 + }, + "status": { + "enum": [ + "connected", + "planned", + "decommissioning" + ], + "type": "string", + "description": "* `connected` - Connected\n* `planned` - Planned\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "80d251a40f3a3144" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "auth_type": { + "enum": [ + "open", + "wep", + "wpa-personal", + "wpa-enterprise", + "" + ], + "type": "string", + "description": "* `open` - Open\n* `wep` - WEP\n* `wpa-personal` - WPA Personal (PSK)\n* `wpa-enterprise` - WPA Enterprise", + "x-spec-enum-id": "e917c12aac765910" + }, + "auth_cipher": { + "enum": [ + "auto", + "tkip", + "aes", + "" + ], + "type": "string", + "description": "* `auto` - Auto\n* `tkip` - TKIP\n* `aes` - AES", + "x-spec-enum-id": "42f867e89988bb0c" + }, + "auth_psk": { + "type": "string", + "title": "Pre-shared key", + "maxLength": 64 + }, + "distance": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "distance_unit": { + "enum": [ + "km", + "m", + "mi", + "ft", + "", + null + ], + "type": "string", + "description": "* `km` - Kilometers\n* `m` - Meters\n* `mi` - Miles\n* `ft` - Feet", + "x-spec-enum-id": "b1169a409430c02e", + "nullable": true + }, + "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": [ + "id", + "interface_a", + "interface_b" + ] + }, "Cable": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -244195,6 +264467,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -244229,8 +264505,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "label": { "type": "string", @@ -244261,6 +264537,10 @@ "LC/PC", "LC/UPC", "LC/APC", + "MU", + "MU/PC", + "MU/UPC", + "MU/APC", "LSH", "LSH/PC", "LSH/UPC", @@ -244516,6 +264796,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -244550,8 +264834,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -244684,6 +264968,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -244718,8 +265006,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "label": { "type": "string", @@ -244750,6 +265038,10 @@ "LC/PC", "LC/UPC", "LC/APC", + "MU", + "MU/PC", + "MU/UPC", + "MU/APC", "LSH", "LSH/PC", "LSH/UPC", @@ -244946,6 +265238,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -244980,8 +265276,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -259305,6 +279601,12959 @@ } } }, + "PatchedBulkASNRangeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "rir": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefRIRRequest" + } + ] + }, + "start": { + "type": "integer", + "maximum": 4294967295, + "minimum": 1, + "format": "int64" + }, + "end": { + "type": "integer", + "maximum": 4294967295, + "minimum": 1, + "format": "int64" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkASNRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "asn": { + "type": "integer", + "maximum": 4294967295, + "minimum": 1, + "format": "int64", + "description": "16- or 32-bit autonomous system number" + }, + "rir": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRIRRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": {} + }, + "sites": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkAggregateRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "prefix": { + "type": "string", + "minLength": 1 + }, + "rir": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefRIRRequest" + } + ] + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "date_added": { + "type": "string", + "format": "date", + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkBookmarkRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkCableBundleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkCableRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "type": { + "enum": [ + "cat3", + "cat5", + "cat5e", + "cat6", + "cat6a", + "cat7", + "cat7a", + "cat8", + "mrj21-trunk", + "dac-active", + "dac-passive", + "coaxial", + "rg-6", + "rg-8", + "rg-11", + "rg-59", + "rg-62", + "rg-213", + "lmr-100", + "lmr-200", + "lmr-400", + "mmf", + "mmf-om1", + "mmf-om2", + "mmf-om3", + "mmf-om4", + "mmf-om5", + "smf", + "smf-os1", + "smf-os2", + "aoc", + "power", + "usb", + "", + null + ], + "type": "string", + "description": "* `cat3` - CAT3\n* `cat5` - CAT5\n* `cat5e` - CAT5e\n* `cat6` - CAT6\n* `cat6a` - CAT6a\n* `cat7` - CAT7\n* `cat7a` - CAT7a\n* `cat8` - CAT8\n* `mrj21-trunk` - MRJ21 Trunk\n* `dac-active` - Direct Attach Copper (Active)\n* `dac-passive` - Direct Attach Copper (Passive)\n* `coaxial` - Coaxial\n* `rg-6` - RG-6\n* `rg-8` - RG-8\n* `rg-11` - RG-11\n* `rg-59` - RG-59\n* `rg-62` - RG-62\n* `rg-213` - RG-213\n* `lmr-100` - LMR-100\n* `lmr-200` - LMR-200\n* `lmr-400` - LMR-400\n* `mmf` - Multimode Fiber\n* `mmf-om1` - Multimode Fiber (OM1)\n* `mmf-om2` - Multimode Fiber (OM2)\n* `mmf-om3` - Multimode Fiber (OM3)\n* `mmf-om4` - Multimode Fiber (OM4)\n* `mmf-om5` - Multimode Fiber (OM5)\n* `smf` - Single-mode Fiber\n* `smf-os1` - Single-mode Fiber (OS1)\n* `smf-os2` - Single-mode Fiber (OS2)\n* `aoc` - Active Optical Cabling (AOC)\n* `power` - Power\n* `usb` - USB", + "x-spec-enum-id": "3d4d8d7ae24f7be8", + "nullable": true + }, + "a_terminations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GenericObjectRequest" + } + }, + "b_terminations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GenericObjectRequest" + } + }, + "status": { + "enum": [ + "connected", + "planned", + "decommissioning" + ], + "type": "string", + "description": "* `connected` - Connected\n* `planned` - Planned\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "80d251a40f3a3144" + }, + "profile": { + "enum": [ + "single-1c1p", + "single-1c2p", + "single-1c4p", + "single-1c6p", + "single-1c8p", + "single-1c12p", + "single-1c16p", + "trunk-2c1p", + "trunk-2c2p", + "trunk-2c4p", + "trunk-2c4p-shuffle", + "trunk-2c6p", + "trunk-2c8p", + "trunk-2c12p", + "trunk-4c1p", + "trunk-4c2p", + "trunk-4c4p", + "trunk-4c4p-shuffle", + "trunk-4c6p", + "trunk-4c8p", + "trunk-8c4p", + "breakout-1c2p-2c1p", + "breakout-1c4p-4c1p", + "breakout-1c6p-6c1p", + "breakout-2c4p-8c1p-shuffle" + ], + "type": "string", + "description": "* `single-1c1p` - 1C1P\n* `single-1c2p` - 1C2P\n* `single-1c4p` - 1C4P\n* `single-1c6p` - 1C6P\n* `single-1c8p` - 1C8P\n* `single-1c12p` - 1C12P\n* `single-1c16p` - 1C16P\n* `trunk-2c1p` - 2C1P trunk\n* `trunk-2c2p` - 2C2P trunk\n* `trunk-2c4p` - 2C4P trunk\n* `trunk-2c4p-shuffle` - 2C4P trunk (shuffle)\n* `trunk-2c6p` - 2C6P trunk\n* `trunk-2c8p` - 2C8P trunk\n* `trunk-2c12p` - 2C12P trunk\n* `trunk-4c1p` - 4C1P trunk\n* `trunk-4c2p` - 4C2P trunk\n* `trunk-4c4p` - 4C4P trunk\n* `trunk-4c4p-shuffle` - 4C4P trunk (shuffle)\n* `trunk-4c6p` - 4C6P trunk\n* `trunk-4c8p` - 4C8P trunk\n* `trunk-8c4p` - 8C4P trunk\n* `breakout-1c2p-2c1p` - 1C2P:2C1P breakout\n* `breakout-1c4p-4c1p` - 1C4P:4C1P breakout\n* `breakout-1c6p-6c1p` - 1C6P:6C1P breakout\n* `breakout-2c4p-8c1p-shuffle` - 2C4P:8C1P breakout (shuffle)", + "x-spec-enum-id": "f566e6df6572f5d0" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "bundle": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCableBundleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "label": { + "type": "string", + "maxLength": 100 + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "length": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "length_unit": { + "enum": [ + "km", + "m", + "cm", + "mi", + "ft", + "in", + "", + null + ], + "type": "string", + "description": "* `km` - Kilometers\n* `m` - Meters\n* `cm` - Centimeters\n* `mi` - Miles\n* `ft` - Feet\n* `in` - Inches", + "x-spec-enum-id": "6e7645525ba02462", + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkCircuitGroupAssignmentRequest": { + "type": "object", + "description": "Base serializer for group assignments under CircuitSerializer.", + "properties": { + "id": { + "type": "integer" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefCircuitGroupRequest" + } + ] + }, + "member_type": { + "type": "string" + }, + "member_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "priority": { + "enum": [ + "primary", + "secondary", + "tertiary", + "inactive", + "" + ], + "type": "string", + "description": "* `primary` - Primary\n* `secondary` - Secondary\n* `tertiary` - Tertiary\n* `inactive` - Inactive", + "x-spec-enum-id": "0548fc537440bf9d" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkCircuitGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkCircuitRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "cid": { + "type": "string", + "minLength": 1, + "title": "Circuit ID", + "description": "Unique circuit ID", + "maxLength": 100 + }, + "provider": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderRequest" + } + ] + }, + "provider_account": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefProviderAccountRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefCircuitTypeRequest" + } + ] + }, + "status": { + "enum": [ + "planned", + "provisioning", + "active", + "offline", + "deprovisioning", + "decommissioned" + ], + "type": "string", + "description": "* `planned` - Planned\n* `provisioning` - Provisioning\n* `active` - Active\n* `offline` - Offline\n* `deprovisioning` - Deprovisioning\n* `decommissioned` - Decommissioned", + "x-spec-enum-id": "0a239d878b6666a4" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "install_date": { + "type": "string", + "format": "date", + "nullable": true, + "title": "Installed" + }, + "termination_date": { + "type": "string", + "format": "date", + "nullable": true, + "title": "Terminates" + }, + "commit_rate": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Commit rate (Kbps)", + "description": "Committed rate" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "distance": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "distance_unit": { + "enum": [ + "km", + "m", + "mi", + "ft", + "", + null + ], + "type": "string", + "description": "* `km` - Kilometers\n* `m` - Meters\n* `mi` - Miles\n* `ft` - Feet", + "x-spec-enum-id": "b1169a409430c02e", + "nullable": true + }, + "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": {} + }, + "assignments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BriefCircuitGroupAssignmentSerializer_Request" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkCircuitTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "circuit": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefCircuitRequest" + } + ] + }, + "term_side": { + "enum": [ + "A", + "Z" + ], + "type": "string", + "description": "* `A` - A\n* `Z` - Z", + "x-spec-enum-id": "95b8fcc737f355d0", + "title": "Termination side" + }, + "termination_type": { + "type": "string", + "nullable": true + }, + "termination_id": { + "type": "integer", + "nullable": true + }, + "port_speed": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Port speed (Kbps)", + "description": "Physical circuit speed" + }, + "upstream_speed": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "Upstream speed (Kbps)", + "description": "Upstream speed, if different from port speed" + }, + "xconnect_id": { + "type": "string", + "title": "Cross-connect ID", + "description": "ID of the local cross-connect", + "maxLength": 50 + }, + "pp_info": { + "type": "string", + "title": "Patch panel/port(s)", + "description": "Patch panel ID and port number(s)", + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkCircuitTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id" + ] + }, + "PatchedBulkClusterGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkClusterRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefClusterTypeRequest" + } + ] + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefClusterGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "planned", + "staging", + "active", + "decommissioning", + "offline" + ], + "type": "string", + "description": "* `planned` - Planned\n* `staging` - Staging\n* `active` - Active\n* `decommissioning` - Decommissioning\n* `offline` - Offline", + "x-spec-enum-id": "65a25166053759eb" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkClusterTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkConfigContextProfileRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "schema": { + "nullable": true, + "description": "A JSON schema specifying the structure of the context data for this profile" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "data_source": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkConfigContextRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "profile": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigContextProfileRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "is_active": { + "type": "boolean" + }, + "regions": { + "type": "array", + "items": { + "type": "integer" + } + }, + "site_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "sites": { + "type": "array", + "items": { + "type": "integer" + } + }, + "locations": { + "type": "array", + "items": { + "type": "integer" + } + }, + "device_types": { + "type": "array", + "items": { + "type": "integer" + } + }, + "roles": { + "type": "array", + "items": { + "type": "integer" + } + }, + "platforms": { + "type": "array", + "items": { + "type": "integer" + } + }, + "cluster_types": { + "type": "array", + "items": { + "type": "integer" + } + }, + "cluster_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "clusters": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tenant_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tenants": { + "type": "array", + "items": { + "type": "integer" + } + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "data_source": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + }, + "data": {} + }, + "required": [ + "id" + ] + }, + "PatchedBulkConfigTemplateRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "environment_params": { + "nullable": true, + "title": "Environment parameters", + "description": "Any additional parameters to pass when constructing the Jinja environment" + }, + "template_code": { + "type": "string", + "minLength": 1, + "description": "Jinja template code." + }, + "mime_type": { + "type": "string", + "description": "Defaults to text/plain; charset=utf-8", + "maxLength": 50 + }, + "file_name": { + "type": "string", + "description": "Filename to give to the rendered export file", + "maxLength": 200 + }, + "file_extension": { + "type": "string", + "description": "Extension to append to the rendered filename", + "maxLength": 15 + }, + "as_attachment": { + "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": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + }, + "auto_sync_enabled": { + "type": "boolean", + "description": "Enable automatic synchronization of data when the data file is updated" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkConsolePortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "speed": { + "enum": [ + 1200, + 2400, + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + null + ], + "type": "integer", + "description": "* `1200` - 1200 bps\n* `2400` - 2400 bps\n* `4800` - 4800 bps\n* `9600` - 9600 bps\n* `19200` - 19.2 kbps\n* `38400` - 38.4 kbps\n* `57600` - 57.6 kbps\n* `115200` - 115.2 kbps", + "x-spec-enum-id": "ab6d9635c131a378", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkConsolePortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkConsoleServerPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "speed": { + "enum": [ + 1200, + 2400, + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + null + ], + "type": "integer", + "description": "* `1200` - 1200 bps\n* `2400` - 2400 bps\n* `4800` - 4800 bps\n* `9600` - 9600 bps\n* `19200` - 19.2 kbps\n* `38400` - 38.4 kbps\n* `57600` - 57.6 kbps\n* `115200` - 115.2 kbps", + "x-spec-enum-id": "ab6d9635c131a378", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkConsoleServerPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "de-9", + "db-25", + "rj-11", + "rj-12", + "rj-45", + "mini-din-8", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other", + "" + ], + "type": "string", + "description": "* `de-9` - DE-9\n* `db-25` - DB-25\n* `rj-11` - RJ-11\n* `rj-12` - RJ-12\n* `rj-45` - RJ-45\n* `mini-din-8` - Mini-DIN 8\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "7b8d0e83a4bb5178" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkContactAssignmentRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "contact": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefContactRequest" + } + ] + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefContactRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "priority": { + "enum": [ + "primary", + "secondary", + "tertiary", + "inactive", + "" + ], + "type": "string", + "description": "* `primary` - Primary\n* `secondary` - Secondary\n* `tertiary` - Tertiary\n* `inactive` - Inactive", + "x-spec-enum-id": "0548fc537440bf9d" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkContactGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedContactGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkContactRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "title": { + "type": "string", + "maxLength": 100 + }, + "phone": { + "type": "string", + "maxLength": 50 + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 254 + }, + "address": { + "type": "string", + "maxLength": 200 + }, + "link": { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + "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": [ + "id" + ] + }, + "PatchedBulkContactRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkCustomFieldChoiceSetRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "base_choices": { + "enum": [ + "IATA", + "ISO_3166", + "UN_LOCODE" + ], + "type": "string", + "description": "* `IATA` - IATA (Airport codes)\n* `ISO_3166` - ISO 3166 (Country codes)\n* `UN_LOCODE` - UN/LOCODE (Location codes)", + "x-spec-enum-id": "cf0efb5195f85007" + }, + "extra_choices": { + "type": "array", + "items": { + "type": "array", + "items": {}, + "maxItems": 2, + "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" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkCustomFieldRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "enum": [ + "text", + "longtext", + "integer", + "decimal", + "boolean", + "date", + "datetime", + "url", + "json", + "select", + "multiselect", + "object", + "multiobject" + ], + "type": "string", + "description": "* `text` - Text\n* `longtext` - Text (long)\n* `integer` - Integer\n* `decimal` - Decimal\n* `boolean` - Boolean (true/false)\n* `date` - Date\n* `datetime` - Date & time\n* `url` - URL\n* `json` - JSON\n* `select` - Selection\n* `multiselect` - Multiple selection\n* `object` - Object\n* `multiobject` - Multiple objects", + "x-spec-enum-id": "47c52a3d983e924c" + }, + "related_object_type": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Internal field name", + "pattern": "^[a-z0-9_]+$", + "maxLength": 50 + }, + "label": { + "type": "string", + "description": "Name of the field as displayed to users (if not provided, 'the field's name will be used)", + "maxLength": 50 + }, + "group_name": { + "type": "string", + "description": "Custom fields within the same group will be displayed together", + "maxLength": 50 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "required": { + "type": "boolean", + "description": "This field is required when creating new objects or editing an existing object." + }, + "unique": { + "type": "boolean", + "title": "Must be unique", + "description": "The value of this field must be unique for the assigned object" + }, + "search_weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "description": "Weighting for search. Lower values are considered more important. Fields with a search weight of zero will be ignored." + }, + "filter_logic": { + "enum": [ + "disabled", + "loose", + "exact" + ], + "type": "string", + "description": "* `disabled` - Disabled\n* `loose` - Loose\n* `exact` - Exact", + "x-spec-enum-id": "d168820c798ae45a" + }, + "ui_visible": { + "enum": [ + "always", + "if-set", + "hidden" + ], + "type": "string", + "description": "* `always` - Always\n* `if-set` - If set\n* `hidden` - Hidden", + "x-spec-enum-id": "f32800c399b927b6" + }, + "ui_editable": { + "enum": [ + "yes", + "no", + "hidden" + ], + "type": "string", + "description": "* `yes` - Yes\n* `no` - No\n* `hidden` - Hidden", + "x-spec-enum-id": "336f52760e62022f" + }, + "is_cloneable": { + "type": "boolean", + "description": "Replicate this value when cloning objects" + }, + "default": { + "nullable": true, + "description": "Default value for the field (must be a JSON value). Encapsulate strings with double quotes (e.g. \"Foo\")." + }, + "related_object_filter": { + "nullable": true, + "description": "Filter the object selection choices using a query_params dict (must be a JSON value).Encapsulate strings with double quotes (e.g. \"Foo\")." + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "title": "Display weight", + "description": "Fields with higher weights appear lower in a form." + }, + "validation_minimum": { + "type": "number", + "format": "double", + "maximum": 1000000000000, + "minimum": -1000000000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Minimum value", + "description": "Minimum allowed value (for numeric fields)" + }, + "validation_maximum": { + "type": "number", + "format": "double", + "maximum": 1000000000000, + "minimum": -1000000000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Maximum value", + "description": "Maximum allowed value (for numeric fields)" + }, + "validation_regex": { + "type": "string", + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefCustomFieldChoiceSetRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkCustomLinkRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "enabled": { + "type": "boolean" + }, + "link_text": { + "type": "string", + "minLength": 1, + "description": "Jinja2 template code for link text" + }, + "link_url": { + "type": "string", + "minLength": 1, + "description": "Jinja2 template code for link URL" + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "group_name": { + "type": "string", + "description": "Links with the same group will appear as a dropdown menu", + "maxLength": 50 + }, + "button_class": { + "enum": [ + "default", + "blue", + "indigo", + "purple", + "pink", + "red", + "orange", + "yellow", + "green", + "teal", + "cyan", + "gray", + "black", + "white", + "ghost-dark" + ], + "type": "string", + "x-spec-enum-id": "5e54b3bd086685ce", + "description": "The class of the first link in a group will be used for the dropdown button\n\n* `default` - Default\n* `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\n* `ghost-dark` - Link" + }, + "new_window": { + "type": "boolean", + "description": "Force link to open in a new window" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkDataSourceRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "type": { + "enum": [ + null, + "local", + "git", + "amazon-s3" + ], + "description": "* `None` - ---------\n* `local` - Local\n* `git` - Git\n* `amazon-s3` - Amazon S3", + "x-spec-enum-id": "562b613a749b34b0" + }, + "source_url": { + "type": "string", + "minLength": 1, + "title": "URL", + "maxLength": 200 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "sync_interval": { + "enum": [ + 1, + 60, + 720, + 1440, + 10080, + 43200, + null + ], + "type": "integer", + "description": "* `1` - Minutely\n* `60` - Hourly\n* `720` - 12 hours\n* `1440` - Daily\n* `10080` - Weekly\n* `43200` - 30 days", + "x-spec-enum-id": "2e9f2567ecd93fbe", + "nullable": true, + "minimum": 0, + "maximum": 32767 + }, + "parameters": { + "nullable": true + }, + "ignore_rules": { + "type": "string", + "description": "Patterns (one per line) matching files or paths to ignore when syncing" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkDeviceBayRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "installed_device": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkDeviceBayTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkDeviceRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "vm_role": { + "type": "boolean", + "description": "Virtual machines may be assigned to this role" + }, + "config_template": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedDeviceRoleRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkDeviceTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ] + }, + "default_platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "model": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "part_number": { + "type": "string", + "description": "Discrete part number (optional)", + "maxLength": 50 + }, + "u_height": { + "type": "number", + "format": "double", + "maximum": 1000, + "minimum": 0.0, + "exclusiveMaximum": true, + "default": 1.0, + "title": "Position (U)" + }, + "exclude_from_utilization": { + "type": "boolean", + "description": "Devices of this type are excluded when calculating rack utilization." + }, + "is_full_depth": { + "type": "boolean", + "description": "Device consumes both front and rear rack faces." + }, + "subdevice_role": { + "enum": [ + "parent", + "child", + "", + null + ], + "type": "string", + "description": "* `parent` - Parent\n* `child` - Child", + "x-spec-enum-id": "65a61d5e1deb4a24", + "nullable": true + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "rear-to-side", + "bottom-to-top", + "top-to-bottom", + "passive", + "mixed", + "", + null + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front\n* `left-to-right` - Left to right\n* `right-to-left` - Right to left\n* `side-to-rear` - Side to rear\n* `rear-to-side` - Rear to side\n* `bottom-to-top` - Bottom to top\n* `top-to-bottom` - Top to bottom\n* `passive` - Passive\n* `mixed` - Mixed", + "x-spec-enum-id": "11cb3d363b41ba9e", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "front_image": { + "type": "string", + "format": "binary", + "nullable": true + }, + "rear_image": { + "type": "string", + "format": "binary", + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkDeviceWithConfigContextRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "nullable": true, + "maxLength": 64 + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ] + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRoleRequest" + } + ] + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "serial": { + "type": "string", + "title": "Serial number", + "description": "Chassis serial number, assigned by the manufacturer", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this device", + "maxLength": 50 + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "location": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefLocationRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "rack": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "position": { + "type": "number", + "format": "double", + "maximum": 1000, + "minimum": 0.5, + "exclusiveMaximum": true, + "nullable": true, + "title": "Position (U)" + }, + "face": { + "enum": [ + "front", + "rear", + "" + ], + "type": "string", + "description": "* `front` - Front\n* `rear` - Rear", + "x-spec-enum-id": "d2fb9b3f75158b83" + }, + "latitude": { + "type": "number", + "format": "double", + "maximum": 90.0, + "minimum": -90.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "longitude": { + "type": "number", + "format": "double", + "maximum": 180.0, + "minimum": -180.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "staged", + "failed", + "inventory", + "decommissioning" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `inventory` - Inventory\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "65feb4244cc9110c" + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "rear-to-side", + "bottom-to-top", + "top-to-bottom", + "passive", + "mixed", + "" + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front\n* `left-to-right` - Left to right\n* `right-to-left` - Right to left\n* `side-to-rear` - Side to rear\n* `rear-to-side` - Rear to side\n* `bottom-to-top` - Bottom to top\n* `top-to-bottom` - Top to bottom\n* `passive` - Passive\n* `mixed` - Mixed", + "x-spec-enum-id": "11cb3d363b41ba9e" + }, + "primary_ip4": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip6": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "oob_ip": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "cluster": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefClusterRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "virtual_chassis": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVirtualChassisRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vc_position": { + "type": "integer", + "maximum": 255, + "minimum": 0, + "nullable": true + }, + "vc_priority": { + "type": "integer", + "maximum": 255, + "minimum": 0, + "nullable": true, + "description": "Virtual chassis master election priority" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "config_template": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "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": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkEventRuleRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 150 + }, + "enabled": { + "type": "boolean" + }, + "event_types": { + "type": "array", + "items": { + "enum": [ + "object_created", + "object_updated", + "object_deleted", + "job_started", + "job_completed", + "job_failed", + "job_errored" + ], + "type": "string", + "description": "* `object_created` - Object created\n* `object_updated` - Object updated\n* `object_deleted` - Object deleted\n* `job_started` - Job started\n* `job_completed` - Job completed\n* `job_failed` - Job failed\n* `job_errored` - Job errored", + "x-spec-enum-id": "01e557313a5c7bd2" + }, + "description": "The types of event which will trigger this rule." + }, + "conditions": { + "nullable": true, + "description": "A set of conditions which determine whether the event will be generated." + }, + "action_type": { + "enum": [ + "webhook", + "script", + "notification" + ], + "type": "string", + "description": "* `webhook` - Webhook\n* `script` - Script\n* `notification` - Notification", + "x-spec-enum-id": "287901b937995956" + }, + "action_object_type": { + "type": "string" + }, + "action_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkExportTemplateRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "environment_params": { + "nullable": true, + "title": "Environment parameters", + "description": "Any additional parameters to pass when constructing the Jinja environment" + }, + "template_code": { + "type": "string", + "minLength": 1, + "description": "Jinja template code." + }, + "mime_type": { + "type": "string", + "description": "Defaults to text/plain; charset=utf-8", + "maxLength": 50 + }, + "file_name": { + "type": "string", + "description": "Filename to give to the rendered export file", + "maxLength": 200 + }, + "file_extension": { + "type": "string", + "description": "Extension to append to the rendered filename", + "maxLength": 15 + }, + "as_attachment": { + "type": "boolean", + "description": "Download file as attachment" + }, + "data_source": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDataSourceRequest" + } + ] + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkFHRPGroupAssignmentRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefFHRPGroupRequest" + } + ] + }, + "interface_type": { + "type": "string" + }, + "interface_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "priority": { + "type": "integer", + "maximum": 255, + "minimum": 0 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkFHRPGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "protocol": { + "enum": [ + "vrrp2", + "vrrp3", + "carp", + "clusterxl", + "hsrp", + "glbp", + "other" + ], + "type": "string", + "description": "* `vrrp2` - VRRPv2\n* `vrrp3` - VRRPv3\n* `carp` - CARP\n* `clusterxl` - ClusterXL\n* `hsrp` - HSRP\n* `glbp` - GLBP\n* `other` - Other", + "x-spec-enum-id": "98de93c9f65d1c65" + }, + "group_id": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "auth_type": { + "enum": [ + "plaintext", + "md5", + "", + null + ], + "type": "string", + "description": "* `plaintext` - Plaintext\n* `md5` - MD5", + "x-spec-enum-id": "565396e386e1542a", + "nullable": true, + "title": "Authentication type" + }, + "auth_key": { + "type": "string", + "title": "Authentication key", + "maxLength": 255 + }, + "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": [ + "id" + ] + }, + "PatchedBulkFrontPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "rear_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FrontPortMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkFrontPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "rear_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FrontPortTemplateMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkGroupRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 150 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "permissions": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkIKEPolicyRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "version": { + "enum": [ + 1, + 2 + ], + "type": "integer", + "description": "* `1` - IKEv1\n* `2` - IKEv2", + "x-spec-enum-id": "00872b77916a1fde" + }, + "mode": { + "enum": [ + "aggressive", + "main" + ], + "type": "string", + "description": "* `aggressive` - Aggressive\n* `main` - Main", + "x-spec-enum-id": "64c1be7bdb2548ca" + }, + "proposals": { + "type": "array", + "items": { + "type": "integer" + } + }, + "preshared_key": { + "type": "string", + "title": "Pre-shared key" + }, + "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": [ + "id" + ] + }, + "PatchedBulkIKEProposalRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "authentication_method": { + "enum": [ + "preshared-keys", + "certificates", + "rsa-signatures", + "dsa-signatures" + ], + "type": "string", + "description": "* `preshared-keys` - Pre-shared keys\n* `certificates` - Certificates\n* `rsa-signatures` - RSA signatures\n* `dsa-signatures` - DSA signatures", + "x-spec-enum-id": "a21158c52d0c455a" + }, + "encryption_algorithm": { + "enum": [ + "aes-128-cbc", + "aes-128-gcm", + "aes-192-cbc", + "aes-192-gcm", + "aes-256-cbc", + "aes-256-gcm", + "3des-cbc", + "des-cbc" + ], + "type": "string", + "description": "* `aes-128-cbc` - 128-bit AES (CBC)\n* `aes-128-gcm` - 128-bit AES (GCM)\n* `aes-192-cbc` - 192-bit AES (CBC)\n* `aes-192-gcm` - 192-bit AES (GCM)\n* `aes-256-cbc` - 256-bit AES (CBC)\n* `aes-256-gcm` - 256-bit AES (GCM)\n* `3des-cbc` - 3DES\n* `des-cbc` - DES", + "x-spec-enum-id": "ae3dabd7b2b3cba2" + }, + "authentication_algorithm": { + "enum": [ + "hmac-sha1", + "hmac-sha256", + "hmac-sha384", + "hmac-sha512", + "hmac-md5" + ], + "type": "string", + "description": "* `hmac-sha1` - SHA-1 HMAC\n* `hmac-sha256` - SHA-256 HMAC\n* `hmac-sha384` - SHA-384 HMAC\n* `hmac-sha512` - SHA-512 HMAC\n* `hmac-md5` - MD5 HMAC", + "x-spec-enum-id": "0a7ca69695b483a7" + }, + "group": { + "enum": [ + 1, + 2, + 5, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34 + ], + "type": "integer", + "description": "* `1` - Group 1\n* `2` - Group 2\n* `5` - Group 5\n* `14` - Group 14\n* `15` - Group 15\n* `16` - Group 16\n* `17` - Group 17\n* `18` - Group 18\n* `19` - Group 19\n* `20` - Group 20\n* `21` - Group 21\n* `22` - Group 22\n* `23` - Group 23\n* `24` - Group 24\n* `25` - Group 25\n* `26` - Group 26\n* `27` - Group 27\n* `28` - Group 28\n* `29` - Group 29\n* `30` - Group 30\n* `31` - Group 31\n* `32` - Group 32\n* `33` - Group 33\n* `34` - Group 34", + "x-spec-enum-id": "dbef43be795462a8" + }, + "sa_lifetime": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "description": "Security association lifetime (in seconds)" + }, + "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": [ + "id" + ] + }, + "PatchedBulkIPAddressRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "address": { + "type": "string", + "minLength": 1 + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "deprecated", + "dhcp", + "slaac" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated\n* `dhcp` - DHCP\n* `slaac` - SLAAC", + "x-spec-enum-id": "c421c4c4a0fa7a2a" + }, + "role": { + "enum": [ + "loopback", + "secondary", + "anycast", + "vip", + "vrrp", + "hsrp", + "glbp", + "carp", + "" + ], + "type": "string", + "description": "* `loopback` - Loopback\n* `secondary` - Secondary\n* `anycast` - Anycast\n* `vip` - VIP\n* `vrrp` - VRRP\n* `hsrp` - HSRP\n* `glbp` - GLBP\n* `carp` - CARP", + "x-spec-enum-id": "53dca4cddd7b344a" + }, + "assigned_object_type": { + "type": "string", + "nullable": true + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "nat_inside": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedIPAddressRequest" + } + ], + "nullable": true + }, + "dns_name": { + "type": "string", + "description": "Hostname or FQDN (not case-sensitive)", + "pattern": "^([0-9A-Za-z_-]+|\\*)(\\.[0-9A-Za-z_-]+)*\\.?$", + "maxLength": 255 + }, + "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": [ + "id" + ] + }, + "PatchedBulkIPRangeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "start_address": { + "type": "string", + "minLength": 1 + }, + "end_address": { + "type": "string", + "minLength": 1 + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "deprecated" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated", + "x-spec-enum-id": "ca933c38b935e547" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": {} + }, + "mark_populated": { + "type": "boolean", + "description": "Prevent the creation of IP addresses within this range" + }, + "mark_utilized": { + "type": "boolean", + "description": "Report space as fully utilized" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkIPSecPolicyRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "proposals": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pfs_group": { + "enum": [ + 1, + 2, + 5, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34 + ], + "type": "integer", + "description": "* `1` - Group 1\n* `2` - Group 2\n* `5` - Group 5\n* `14` - Group 14\n* `15` - Group 15\n* `16` - Group 16\n* `17` - Group 17\n* `18` - Group 18\n* `19` - Group 19\n* `20` - Group 20\n* `21` - Group 21\n* `22` - Group 22\n* `23` - Group 23\n* `24` - Group 24\n* `25` - Group 25\n* `26` - Group 26\n* `27` - Group 27\n* `28` - Group 28\n* `29` - Group 29\n* `30` - Group 30\n* `31` - Group 31\n* `32` - Group 32\n* `33` - Group 33\n* `34` - Group 34", + "x-spec-enum-id": "dbef43be795462a8" + }, + "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": [ + "id" + ] + }, + "PatchedBulkIPSecProfileRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mode": { + "enum": [ + "esp", + "ah" + ], + "type": "string", + "description": "* `esp` - ESP\n* `ah` - AH", + "x-spec-enum-id": "87ac6ada0da14ccf" + }, + "ike_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefIKEPolicyRequest" + } + ] + }, + "ipsec_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefIPSecPolicyRequest" + } + ] + }, + "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": [ + "id" + ] + }, + "PatchedBulkIPSecProposalRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "encryption_algorithm": { + "enum": [ + "aes-128-cbc", + "aes-128-gcm", + "aes-192-cbc", + "aes-192-gcm", + "aes-256-cbc", + "aes-256-gcm", + "3des-cbc", + "des-cbc" + ], + "type": "string", + "description": "* `aes-128-cbc` - 128-bit AES (CBC)\n* `aes-128-gcm` - 128-bit AES (GCM)\n* `aes-192-cbc` - 192-bit AES (CBC)\n* `aes-192-gcm` - 192-bit AES (GCM)\n* `aes-256-cbc` - 256-bit AES (CBC)\n* `aes-256-gcm` - 256-bit AES (GCM)\n* `3des-cbc` - 3DES\n* `des-cbc` - DES", + "x-spec-enum-id": "ae3dabd7b2b3cba2" + }, + "authentication_algorithm": { + "enum": [ + "hmac-sha1", + "hmac-sha256", + "hmac-sha384", + "hmac-sha512", + "hmac-md5" + ], + "type": "string", + "description": "* `hmac-sha1` - SHA-1 HMAC\n* `hmac-sha256` - SHA-256 HMAC\n* `hmac-sha384` - SHA-384 HMAC\n* `hmac-sha512` - SHA-512 HMAC\n* `hmac-md5` - MD5 HMAC", + "x-spec-enum-id": "0a7ca69695b483a7" + }, + "sa_lifetime_seconds": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "SA lifetime (seconds)", + "description": "Security association lifetime (seconds)" + }, + "sa_lifetime_data": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "title": "SA lifetime (KB)", + "description": "Security association lifetime (in kilobytes)" + }, + "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": [ + "id" + ] + }, + "PatchedBulkImageAttachmentRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "name": { + "type": "string", + "maxLength": 50 + }, + "image": { + "type": "string", + "format": "binary" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkInterfaceRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "vdcs": { + "type": "array", + "items": { + "type": "integer" + } + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "virtual", + "bridge", + "lag", + "100base-fx", + "100base-lfx", + "100base-tx", + "100base-t1", + "1000base-bx10-d", + "1000base-bx10-u", + "1000base-cwdm", + "1000base-cx", + "1000base-dwdm", + "1000base-ex", + "1000base-lsx", + "1000base-lx", + "1000base-lx10", + "1000base-sx", + "1000base-t", + "1000base-tx", + "1000base-zx", + "2.5gbase-t", + "5gbase-t", + "10gbase-br-d", + "10gbase-br-u", + "10gbase-cu", + "10gbase-cx4", + "10gbase-er", + "10gbase-lr", + "10gbase-lrm", + "10gbase-lx4", + "10gbase-sr", + "10gbase-t", + "10gbase-zr", + "25gbase-cr", + "25gbase-er", + "25gbase-lr", + "25gbase-sr", + "25gbase-t", + "40gbase-cr4", + "40gbase-er4", + "40gbase-fr4", + "40gbase-lr4", + "40gbase-sr4", + "40gbase-sr4-bd", + "50gbase-cr", + "50gbase-er", + "50gbase-fr", + "50gbase-lr", + "50gbase-sr", + "100gbase-cr1", + "100gbase-cr2", + "100gbase-cr4", + "100gbase-cr10", + "100gbase-cwdm4", + "100gbase-dr", + "100gbase-er4", + "100gbase-fr1", + "100gbase-lr1", + "100gbase-lr4", + "100gbase-sr1", + "100gbase-sr1.2", + "100gbase-sr2", + "100gbase-sr4", + "100gbase-sr10", + "100gbase-zr", + "200gbase-cr2", + "200gbase-cr4", + "200gbase-dr4", + "200gbase-er4", + "200gbase-fr4", + "200gbase-lr4", + "200gbase-sr2", + "200gbase-sr4", + "200gbase-vr2", + "400gbase-cr4", + "400gbase-dr4", + "400gbase-er8", + "400gbase-fr4", + "400gbase-fr8", + "400gbase-lr4", + "400gbase-lr8", + "400gbase-sr4", + "400gbase-sr4_2", + "400gbase-sr8", + "400gbase-sr16", + "400gbase-vr4", + "400gbase-zr", + "800gbase-cr8", + "800gbase-dr8", + "800gbase-sr8", + "800gbase-vr8", + "1.6tbase-cr8", + "1.6tbase-dr8", + "1.6tbase-dr8-2", + "100base-x-sfp", + "1000base-x-gbic", + "1000base-x-sfp", + "2.5gbase-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xenpak", + "10gbase-x-xfp", + "10gbase-x-x2", + "25gbase-x-sfp28", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "50gbase-x-sfp56", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cxp", + "100gbase-x-cpak", + "100gbase-x-dsfp", + "100gbase-x-qsfp28", + "100gbase-x-qsfpdd", + "100gbase-x-sfpdd", + "200gbase-x-cfp2", + "200gbase-x-qsfp56", + "200gbase-x-qsfpdd", + "400gbase-x-qsfp112", + "400gbase-x-qsfpdd", + "400gbase-x-cdfp", + "400gbase-x-cfp2", + "400gbase-x-cfp8", + "400gbase-x-osfp", + "400gbase-x-osfp-rhs", + "800gbase-x-osfp", + "800gbase-x-qsfpdd", + "1.6tbase-x-osfp1600", + "1.6tbase-x-osfp1600-rhs", + "1.6tbase-x-qsfpdd1600", + "1000base-kx", + "2.5gbase-kx", + "5gbase-kr", + "10gbase-kr", + "10gbase-kx4", + "25gbase-kr", + "40gbase-kr4", + "50gbase-kr", + "100gbase-kp4", + "100gbase-kr2", + "100gbase-kr4", + "1.6tbase-kr8", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "ieee802.11ax", + "ieee802.11ay", + "ieee802.11be", + "ieee802.15.1", + "ieee802.15.4", + "other-wireless", + "gsm", + "cdma", + "lte", + "4g", + "5g", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "32gfc-sfpp", + "64gfc-qsfpp", + "64gfc-sfpdd", + "64gfc-sfpp", + "128gfc-qsfp28", + "infiniband-sdr", + "infiniband-ddr", + "infiniband-qdr", + "infiniband-fdr10", + "infiniband-fdr", + "infiniband-edr", + "infiniband-hdr", + "infiniband-ndr", + "infiniband-xdr", + "t1", + "e1", + "t3", + "e3", + "xdsl", + "docsis", + "moca", + "bpon", + "epon", + "10g-epon", + "gpon", + "xg-pon", + "xgs-pon", + "ng-pon2", + "25g-pon", + "50g-pon", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "cisco-stackwise-80", + "cisco-stackwise-160", + "cisco-stackwise-320", + "cisco-stackwise-480", + "cisco-stackwise-1t", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512", + "other" + ], + "type": "string", + "description": "* `virtual` - Virtual\n* `bridge` - Bridge\n* `lag` - Link Aggregation Group (LAG)\n* `100base-fx` - 100BASE-FX (10/100ME)\n* `100base-lfx` - 100BASE-LFX (10/100ME)\n* `100base-tx` - 100BASE-TX (10/100ME)\n* `100base-t1` - 100BASE-T1 (10/100ME)\n* `1000base-bx10-d` - 1000BASE-BX10-D (1GE BiDi Down)\n* `1000base-bx10-u` - 1000BASE-BX10-U (1GE BiDi Up)\n* `1000base-cwdm` - 1000BASE-CWDM (1GE)\n* `1000base-cx` - 1000BASE-CX (1GE DAC)\n* `1000base-dwdm` - 1000BASE-DWDM (1GE)\n* `1000base-ex` - 1000BASE-EX (1GE)\n* `1000base-lsx` - 1000BASE-LSX (1GE)\n* `1000base-lx` - 1000BASE-LX (1GE)\n* `1000base-lx10` - 1000BASE-LX10/LH (1GE)\n* `1000base-sx` - 1000BASE-SX (1GE)\n* `1000base-t` - 1000BASE-T (1GE)\n* `1000base-tx` - 1000BASE-TX (1GE)\n* `1000base-zx` - 1000BASE-ZX (1GE)\n* `2.5gbase-t` - 2.5GBASE-T (2.5GE)\n* `5gbase-t` - 5GBASE-T (5GE)\n* `10gbase-br-d` - 10GBASE-BR-D (10GE BiDi Down)\n* `10gbase-br-u` - 10GBASE-BR-U (10GE BiDi Up)\n* `10gbase-cu` - 10GBASE-CU (10GE DAC Passive Twinax)\n* `10gbase-cx4` - 10GBASE-CX4 (10GE DAC)\n* `10gbase-er` - 10GBASE-ER (10GE)\n* `10gbase-lr` - 10GBASE-LR (10GE)\n* `10gbase-lrm` - 10GBASE-LRM (10GE)\n* `10gbase-lx4` - 10GBASE-LX4 (10GE)\n* `10gbase-sr` - 10GBASE-SR (10GE)\n* `10gbase-t` - 10GBASE-T (10GE)\n* `10gbase-zr` - 10GBASE-ZR (10GE)\n* `25gbase-cr` - 25GBASE-CR (25GE DAC)\n* `25gbase-er` - 25GBASE-ER (25GE)\n* `25gbase-lr` - 25GBASE-LR (25GE)\n* `25gbase-sr` - 25GBASE-SR (25GE)\n* `25gbase-t` - 25GBASE-T (25GE)\n* `40gbase-cr4` - 40GBASE-CR4 (40GE DAC)\n* `40gbase-er4` - 40GBASE-ER4 (40GE)\n* `40gbase-fr4` - 40GBASE-FR4 (40GE)\n* `40gbase-lr4` - 40GBASE-LR4 (40GE)\n* `40gbase-sr4` - 40GBASE-SR4 (40GE)\n* `40gbase-sr4-bd` - 40GBASE-SR4 (40GE BiDi)\n* `50gbase-cr` - 50GBASE-CR (50GE DAC)\n* `50gbase-er` - 50GBASE-ER (50GE)\n* `50gbase-fr` - 50GBASE-FR (50GE)\n* `50gbase-lr` - 50GBASE-LR (50GE)\n* `50gbase-sr` - 50GBASE-SR (50GE)\n* `100gbase-cr1` - 100GBASE-CR1 (100GE DAC)\n* `100gbase-cr2` - 100GBASE-CR2 (100GE DAC)\n* `100gbase-cr4` - 100GBASE-CR4 (100GE DAC)\n* `100gbase-cr10` - 100GBASE-CR10 (100GE DAC)\n* `100gbase-cwdm4` - 100GBASE-CWDM4 (100GE)\n* `100gbase-dr` - 100GBASE-DR (100GE)\n* `100gbase-er4` - 100GBASE-ER4 (100GE)\n* `100gbase-fr1` - 100GBASE-FR1 (100GE)\n* `100gbase-lr1` - 100GBASE-LR1 (100GE)\n* `100gbase-lr4` - 100GBASE-LR4 (100GE)\n* `100gbase-sr1` - 100GBASE-SR1 (100GE)\n* `100gbase-sr1.2` - 100GBASE-SR1.2 (100GE BiDi)\n* `100gbase-sr2` - 100GBASE-SR2 (100GE)\n* `100gbase-sr4` - 100GBASE-SR4 (100GE)\n* `100gbase-sr10` - 100GBASE-SR10 (100GE)\n* `100gbase-zr` - 100GBASE-ZR (100GE)\n* `200gbase-cr2` - 200GBASE-CR2 (200GE)\n* `200gbase-cr4` - 200GBASE-CR4 (200GE)\n* `200gbase-dr4` - 200GBASE-DR4 (200GE)\n* `200gbase-er4` - 200GBASE-ER4 (200GE)\n* `200gbase-fr4` - 200GBASE-FR4 (200GE)\n* `200gbase-lr4` - 200GBASE-LR4 (200GE)\n* `200gbase-sr2` - 200GBASE-SR2 (200GE)\n* `200gbase-sr4` - 200GBASE-SR4 (200GE)\n* `200gbase-vr2` - 200GBASE-VR2 (200GE)\n* `400gbase-cr4` - 400GBASE-CR4 (400GE)\n* `400gbase-dr4` - 400GBASE-DR4 (400GE)\n* `400gbase-er8` - 400GBASE-ER8 (400GE)\n* `400gbase-fr4` - 400GBASE-FR4 (400GE)\n* `400gbase-fr8` - 400GBASE-FR8 (400GE)\n* `400gbase-lr4` - 400GBASE-LR4 (400GE)\n* `400gbase-lr8` - 400GBASE-LR8 (400GE)\n* `400gbase-sr4` - 400GBASE-SR4 (400GE)\n* `400gbase-sr4_2` - 400GBASE-SR4.2 (400GE BiDi)\n* `400gbase-sr8` - 400GBASE-SR8 (400GE)\n* `400gbase-sr16` - 400GBASE-SR16 (400GE)\n* `400gbase-vr4` - 400GBASE-VR4 (400GE)\n* `400gbase-zr` - 400GBASE-ZR (400GE)\n* `800gbase-cr8` - 800GBASE-CR8 (800GE)\n* `800gbase-dr8` - 800GBASE-DR8 (800GE)\n* `800gbase-sr8` - 800GBASE-SR8 (800GE)\n* `800gbase-vr8` - 800GBASE-VR8 (800GE)\n* `1.6tbase-cr8` - 1.6TBASE-CR8 (1.6TE)\n* `1.6tbase-dr8` - 1.6TBASE-DR8 (1.6TE)\n* `1.6tbase-dr8-2` - 1.6TBASE-DR8-2 (1.6TE)\n* `100base-x-sfp` - SFP (100ME)\n* `1000base-x-gbic` - GBIC (1GE)\n* `1000base-x-sfp` - SFP (1GE)\n* `2.5gbase-x-sfp` - SFP (2.5GE)\n* `10gbase-x-sfpp` - SFP+ (10GE)\n* `10gbase-x-xenpak` - XENPAK (10GE)\n* `10gbase-x-xfp` - XFP (10GE)\n* `10gbase-x-x2` - X2 (10GE)\n* `25gbase-x-sfp28` - SFP28 (25GE)\n* `40gbase-x-qsfpp` - QSFP+ (40GE)\n* `50gbase-x-sfp28` - QSFP28 (50GE)\n* `50gbase-x-sfp56` - SFP56 (50GE)\n* `100gbase-x-cfp` - CFP (100GE)\n* `100gbase-x-cfp2` - CFP2 (100GE)\n* `100gbase-x-cfp4` - CFP4 (100GE)\n* `100gbase-x-cxp` - CXP (100GE)\n* `100gbase-x-cpak` - Cisco CPAK (100GE)\n* `100gbase-x-dsfp` - DSFP (100GE)\n* `100gbase-x-qsfp28` - QSFP28 (100GE)\n* `100gbase-x-qsfpdd` - QSFP-DD (100GE)\n* `100gbase-x-sfpdd` - SFP-DD (100GE)\n* `200gbase-x-cfp2` - CFP2 (200GE)\n* `200gbase-x-qsfp56` - QSFP56 (200GE)\n* `200gbase-x-qsfpdd` - QSFP-DD (200GE)\n* `400gbase-x-qsfp112` - QSFP112 (400GE)\n* `400gbase-x-qsfpdd` - QSFP-DD (400GE)\n* `400gbase-x-cdfp` - CDFP (400GE)\n* `400gbase-x-cfp2` - CFP2 (400GE)\n* `400gbase-x-cfp8` - CPF8 (400GE)\n* `400gbase-x-osfp` - OSFP (400GE)\n* `400gbase-x-osfp-rhs` - OSFP-RHS (400GE)\n* `800gbase-x-osfp` - OSFP (800GE)\n* `800gbase-x-qsfpdd` - QSFP-DD (800GE)\n* `1.6tbase-x-osfp1600` - OSFP1600 (1.6TE)\n* `1.6tbase-x-osfp1600-rhs` - OSFP1600-RHS (1.6TE)\n* `1.6tbase-x-qsfpdd1600` - QSFP-DD1600 (1.6TE)\n* `1000base-kx` - 1000BASE-KX (1GE)\n* `2.5gbase-kx` - 2.5GBASE-KX (2.5GE)\n* `5gbase-kr` - 5GBASE-KR (5GE)\n* `10gbase-kr` - 10GBASE-KR (10GE)\n* `10gbase-kx4` - 10GBASE-KX4 (10GE)\n* `25gbase-kr` - 25GBASE-KR (25GE)\n* `40gbase-kr4` - 40GBASE-KR4 (40GE)\n* `50gbase-kr` - 50GBASE-KR (50GE)\n* `100gbase-kp4` - 100GBASE-KP4 (100GE)\n* `100gbase-kr2` - 100GBASE-KR2 (100GE)\n* `100gbase-kr4` - 100GBASE-KR4 (100GE)\n* `1.6tbase-kr8` - 1.6TBASE-KR8 (1.6TE)\n* `ieee802.11a` - IEEE 802.11a\n* `ieee802.11g` - IEEE 802.11b/g\n* `ieee802.11n` - IEEE 802.11n (Wi-Fi 4)\n* `ieee802.11ac` - IEEE 802.11ac (Wi-Fi 5)\n* `ieee802.11ad` - IEEE 802.11ad (WiGig)\n* `ieee802.11ax` - IEEE 802.11ax (Wi-Fi 6)\n* `ieee802.11ay` - IEEE 802.11ay (WiGig)\n* `ieee802.11be` - IEEE 802.11be (Wi-Fi 7)\n* `ieee802.15.1` - IEEE 802.15.1 (Bluetooth)\n* `ieee802.15.4` - IEEE 802.15.4 (LR-WPAN)\n* `other-wireless` - Other (Wireless)\n* `gsm` - GSM\n* `cdma` - CDMA\n* `lte` - LTE\n* `4g` - 4G\n* `5g` - 5G\n* `sonet-oc3` - OC-3/STM-1\n* `sonet-oc12` - OC-12/STM-4\n* `sonet-oc48` - OC-48/STM-16\n* `sonet-oc192` - OC-192/STM-64\n* `sonet-oc768` - OC-768/STM-256\n* `sonet-oc1920` - OC-1920/STM-640\n* `sonet-oc3840` - OC-3840/STM-1234\n* `1gfc-sfp` - SFP (1GFC)\n* `2gfc-sfp` - SFP (2GFC)\n* `4gfc-sfp` - SFP (4GFC)\n* `8gfc-sfpp` - SFP+ (8GFC)\n* `16gfc-sfpp` - SFP+ (16GFC)\n* `32gfc-sfp28` - SFP28 (32GFC)\n* `32gfc-sfpp` - SFP+ (32GFC)\n* `64gfc-qsfpp` - QSFP+ (64GFC)\n* `64gfc-sfpdd` - SFP-DD (64GFC)\n* `64gfc-sfpp` - SFP+ (64GFC)\n* `128gfc-qsfp28` - QSFP28 (128GFC)\n* `infiniband-sdr` - SDR (2 Gbps)\n* `infiniband-ddr` - DDR (4 Gbps)\n* `infiniband-qdr` - QDR (8 Gbps)\n* `infiniband-fdr10` - FDR10 (10 Gbps)\n* `infiniband-fdr` - FDR (13.5 Gbps)\n* `infiniband-edr` - EDR (25 Gbps)\n* `infiniband-hdr` - HDR (50 Gbps)\n* `infiniband-ndr` - NDR (100 Gbps)\n* `infiniband-xdr` - XDR (250 Gbps)\n* `t1` - T1 (1.544 Mbps)\n* `e1` - E1 (2.048 Mbps)\n* `t3` - T3 (45 Mbps)\n* `e3` - E3 (34 Mbps)\n* `xdsl` - xDSL\n* `docsis` - DOCSIS\n* `moca` - MoCA\n* `bpon` - BPON (622 Mbps / 155 Mbps)\n* `epon` - EPON (1 Gbps)\n* `10g-epon` - 10G-EPON (10 Gbps)\n* `gpon` - GPON (2.5 Gbps / 1.25 Gbps)\n* `xg-pon` - XG-PON (10 Gbps / 2.5 Gbps)\n* `xgs-pon` - XGS-PON (10 Gbps)\n* `ng-pon2` - NG-PON2 (TWDM-PON) (4x10 Gbps)\n* `25g-pon` - 25G-PON (25 Gbps)\n* `50g-pon` - 50G-PON (50 Gbps)\n* `cisco-stackwise` - Cisco StackWise\n* `cisco-stackwise-plus` - Cisco StackWise Plus\n* `cisco-flexstack` - Cisco FlexStack\n* `cisco-flexstack-plus` - Cisco FlexStack Plus\n* `cisco-stackwise-80` - Cisco StackWise-80\n* `cisco-stackwise-160` - Cisco StackWise-160\n* `cisco-stackwise-320` - Cisco StackWise-320\n* `cisco-stackwise-480` - Cisco StackWise-480\n* `cisco-stackwise-1t` - Cisco StackWise-1T\n* `juniper-vcp` - Juniper VCP\n* `extreme-summitstack` - Extreme SummitStack\n* `extreme-summitstack-128` - Extreme SummitStack-128\n* `extreme-summitstack-256` - Extreme SummitStack-256\n* `extreme-summitstack-512` - Extreme SummitStack-512\n* `other` - Other", + "x-spec-enum-id": "b067eb1f050c6ae9" + }, + "enabled": { + "type": "boolean" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceRequest" + } + ], + "nullable": true + }, + "bridge": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceRequest" + } + ], + "nullable": true + }, + "lag": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceRequest" + } + ], + "nullable": true + }, + "mtu": { + "type": "integer", + "maximum": 65536, + "minimum": 1, + "nullable": true + }, + "primary_mac_address": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefMACAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "speed": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true, + "title": "Speed (Kbps)" + }, + "duplex": { + "enum": [ + "half", + "full", + "auto", + "", + null + ], + "type": "string", + "description": "* `half` - Half\n* `full` - Full\n* `auto` - Auto", + "x-spec-enum-id": "368458a2b67c916b", + "nullable": true + }, + "wwn": { + "type": "string", + "nullable": true + }, + "mgmt_only": { + "type": "boolean", + "title": "Management only", + "description": "This interface is used only for out-of-band management" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mode": { + "enum": [ + "access", + "tagged", + "tagged-all", + "q-in-q", + "" + ], + "type": "string", + "description": "* `access` - Access\n* `tagged` - Tagged\n* `tagged-all` - Tagged (All)\n* `q-in-q` - Q-in-Q (802.1ad)", + "x-spec-enum-id": "84129b71b974ebe5" + }, + "rf_role": { + "enum": [ + "ap", + "station", + "" + ], + "type": "string", + "description": "* `ap` - Access point\n* `station` - Station", + "x-spec-enum-id": "d2772dbea88b0fb1" + }, + "rf_channel": { + "enum": [ + "2.4g-1-2412-22", + "2.4g-2-2417-22", + "2.4g-3-2422-22", + "2.4g-4-2427-22", + "2.4g-5-2432-22", + "2.4g-6-2437-22", + "2.4g-7-2442-22", + "2.4g-8-2447-22", + "2.4g-9-2452-22", + "2.4g-10-2457-22", + "2.4g-11-2462-22", + "2.4g-12-2467-22", + "2.4g-13-2472-22", + "5g-32-5160-20", + "5g-34-5170-40", + "5g-36-5180-20", + "5g-38-5190-40", + "5g-40-5200-20", + "5g-42-5210-80", + "5g-44-5220-20", + "5g-46-5230-40", + "5g-48-5240-20", + "5g-50-5250-160", + "5g-52-5260-20", + "5g-54-5270-40", + "5g-56-5280-20", + "5g-58-5290-80", + "5g-60-5300-20", + "5g-62-5310-40", + "5g-64-5320-20", + "5g-100-5500-20", + "5g-102-5510-40", + "5g-104-5520-20", + "5g-106-5530-80", + "5g-108-5540-20", + "5g-110-5550-40", + "5g-112-5560-20", + "5g-114-5570-160", + "5g-116-5580-20", + "5g-118-5590-40", + "5g-120-5600-20", + "5g-122-5610-80", + "5g-124-5620-20", + "5g-126-5630-40", + "5g-128-5640-20", + "5g-132-5660-20", + "5g-134-5670-40", + "5g-136-5680-20", + "5g-138-5690-80", + "5g-140-5700-20", + "5g-142-5710-40", + "5g-144-5720-20", + "5g-149-5745-20", + "5g-151-5755-40", + "5g-153-5765-20", + "5g-155-5775-80", + "5g-157-5785-20", + "5g-159-5795-40", + "5g-161-5805-20", + "5g-163-5815-160", + "5g-165-5825-20", + "5g-167-5835-40", + "5g-169-5845-20", + "5g-171-5855-80", + "5g-173-5865-20", + "5g-175-5875-40", + "5g-177-5885-20", + "6g-1-5955-20", + "6g-3-5965-40", + "6g-5-5975-20", + "6g-7-5985-80", + "6g-9-5995-20", + "6g-11-6005-40", + "6g-13-6015-20", + "6g-15-6025-160", + "6g-17-6035-20", + "6g-19-6045-40", + "6g-21-6055-20", + "6g-23-6065-80", + "6g-25-6075-20", + "6g-27-6085-40", + "6g-29-6095-20", + "6g-31-6105-320", + "6g-33-6115-20", + "6g-35-6125-40", + "6g-37-6135-20", + "6g-39-6145-80", + "6g-41-6155-20", + "6g-43-6165-40", + "6g-45-6175-20", + "6g-47-6185-160", + "6g-49-6195-20", + "6g-51-6205-40", + "6g-53-6215-20", + "6g-55-6225-80", + "6g-57-6235-20", + "6g-59-6245-40", + "6g-61-6255-20", + "6g-65-6275-20", + "6g-67-6285-40", + "6g-69-6295-20", + "6g-71-6305-80", + "6g-73-6315-20", + "6g-75-6325-40", + "6g-77-6335-20", + "6g-79-6345-160", + "6g-81-6355-20", + "6g-83-6365-40", + "6g-85-6375-20", + "6g-87-6385-80", + "6g-89-6395-20", + "6g-91-6405-40", + "6g-93-6415-20", + "6g-95-6425-320", + "6g-97-6435-20", + "6g-99-6445-40", + "6g-101-6455-20", + "6g-103-6465-80", + "6g-105-6475-20", + "6g-107-6485-40", + "6g-109-6495-20", + "6g-111-6505-160", + "6g-113-6515-20", + "6g-115-6525-40", + "6g-117-6535-20", + "6g-119-6545-80", + "6g-121-6555-20", + "6g-123-6565-40", + "6g-125-6575-20", + "6g-129-6595-20", + "6g-131-6605-40", + "6g-133-6615-20", + "6g-135-6625-80", + "6g-137-6635-20", + "6g-139-6645-40", + "6g-141-6655-20", + "6g-143-6665-160", + "6g-145-6675-20", + "6g-147-6685-40", + "6g-149-6695-20", + "6g-151-6705-80", + "6g-153-6715-20", + "6g-155-6725-40", + "6g-157-6735-20", + "6g-159-6745-320", + "6g-161-6755-20", + "6g-163-6765-40", + "6g-165-6775-20", + "6g-167-6785-80", + "6g-169-6795-20", + "6g-171-6805-40", + "6g-173-6815-20", + "6g-175-6825-160", + "6g-177-6835-20", + "6g-179-6845-40", + "6g-181-6855-20", + "6g-183-6865-80", + "6g-185-6875-20", + "6g-187-6885-40", + "6g-189-6895-20", + "6g-193-6915-20", + "6g-195-6925-40", + "6g-197-6935-20", + "6g-199-6945-80", + "6g-201-6955-20", + "6g-203-6965-40", + "6g-205-6975-20", + "6g-207-6985-160", + "6g-209-6995-20", + "6g-211-7005-40", + "6g-213-7015-20", + "6g-215-7025-80", + "6g-217-7035-20", + "6g-219-7045-40", + "6g-221-7055-20", + "6g-225-7075-20", + "6g-227-7085-40", + "6g-229-7095-20", + "6g-233-7115-20", + "60g-1-58320-2160", + "60g-2-60480-2160", + "60g-3-62640-2160", + "60g-4-64800-2160", + "60g-5-66960-2160", + "60g-6-69120-2160", + "60g-9-59400-4320", + "60g-10-61560-4320", + "60g-11-63720-4320", + "60g-12-65880-4320", + "60g-13-68040-4320", + "60g-17-60480-6480", + "60g-18-62640-6480", + "60g-19-64800-6480", + "60g-20-66960-6480", + "60g-25-61560-6480", + "60g-26-63720-6480", + "60g-27-65880-6480", + "" + ], + "type": "string", + "description": "* `2.4g-1-2412-22` - 1 (2412 MHz)\n* `2.4g-2-2417-22` - 2 (2417 MHz)\n* `2.4g-3-2422-22` - 3 (2422 MHz)\n* `2.4g-4-2427-22` - 4 (2427 MHz)\n* `2.4g-5-2432-22` - 5 (2432 MHz)\n* `2.4g-6-2437-22` - 6 (2437 MHz)\n* `2.4g-7-2442-22` - 7 (2442 MHz)\n* `2.4g-8-2447-22` - 8 (2447 MHz)\n* `2.4g-9-2452-22` - 9 (2452 MHz)\n* `2.4g-10-2457-22` - 10 (2457 MHz)\n* `2.4g-11-2462-22` - 11 (2462 MHz)\n* `2.4g-12-2467-22` - 12 (2467 MHz)\n* `2.4g-13-2472-22` - 13 (2472 MHz)\n* `5g-32-5160-20` - 32 (5160/20 MHz)\n* `5g-34-5170-40` - 34 (5170/40 MHz)\n* `5g-36-5180-20` - 36 (5180/20 MHz)\n* `5g-38-5190-40` - 38 (5190/40 MHz)\n* `5g-40-5200-20` - 40 (5200/20 MHz)\n* `5g-42-5210-80` - 42 (5210/80 MHz)\n* `5g-44-5220-20` - 44 (5220/20 MHz)\n* `5g-46-5230-40` - 46 (5230/40 MHz)\n* `5g-48-5240-20` - 48 (5240/20 MHz)\n* `5g-50-5250-160` - 50 (5250/160 MHz)\n* `5g-52-5260-20` - 52 (5260/20 MHz)\n* `5g-54-5270-40` - 54 (5270/40 MHz)\n* `5g-56-5280-20` - 56 (5280/20 MHz)\n* `5g-58-5290-80` - 58 (5290/80 MHz)\n* `5g-60-5300-20` - 60 (5300/20 MHz)\n* `5g-62-5310-40` - 62 (5310/40 MHz)\n* `5g-64-5320-20` - 64 (5320/20 MHz)\n* `5g-100-5500-20` - 100 (5500/20 MHz)\n* `5g-102-5510-40` - 102 (5510/40 MHz)\n* `5g-104-5520-20` - 104 (5520/20 MHz)\n* `5g-106-5530-80` - 106 (5530/80 MHz)\n* `5g-108-5540-20` - 108 (5540/20 MHz)\n* `5g-110-5550-40` - 110 (5550/40 MHz)\n* `5g-112-5560-20` - 112 (5560/20 MHz)\n* `5g-114-5570-160` - 114 (5570/160 MHz)\n* `5g-116-5580-20` - 116 (5580/20 MHz)\n* `5g-118-5590-40` - 118 (5590/40 MHz)\n* `5g-120-5600-20` - 120 (5600/20 MHz)\n* `5g-122-5610-80` - 122 (5610/80 MHz)\n* `5g-124-5620-20` - 124 (5620/20 MHz)\n* `5g-126-5630-40` - 126 (5630/40 MHz)\n* `5g-128-5640-20` - 128 (5640/20 MHz)\n* `5g-132-5660-20` - 132 (5660/20 MHz)\n* `5g-134-5670-40` - 134 (5670/40 MHz)\n* `5g-136-5680-20` - 136 (5680/20 MHz)\n* `5g-138-5690-80` - 138 (5690/80 MHz)\n* `5g-140-5700-20` - 140 (5700/20 MHz)\n* `5g-142-5710-40` - 142 (5710/40 MHz)\n* `5g-144-5720-20` - 144 (5720/20 MHz)\n* `5g-149-5745-20` - 149 (5745/20 MHz)\n* `5g-151-5755-40` - 151 (5755/40 MHz)\n* `5g-153-5765-20` - 153 (5765/20 MHz)\n* `5g-155-5775-80` - 155 (5775/80 MHz)\n* `5g-157-5785-20` - 157 (5785/20 MHz)\n* `5g-159-5795-40` - 159 (5795/40 MHz)\n* `5g-161-5805-20` - 161 (5805/20 MHz)\n* `5g-163-5815-160` - 163 (5815/160 MHz)\n* `5g-165-5825-20` - 165 (5825/20 MHz)\n* `5g-167-5835-40` - 167 (5835/40 MHz)\n* `5g-169-5845-20` - 169 (5845/20 MHz)\n* `5g-171-5855-80` - 171 (5855/80 MHz)\n* `5g-173-5865-20` - 173 (5865/20 MHz)\n* `5g-175-5875-40` - 175 (5875/40 MHz)\n* `5g-177-5885-20` - 177 (5885/20 MHz)\n* `6g-1-5955-20` - 1 (5955/20 MHz)\n* `6g-3-5965-40` - 3 (5965/40 MHz)\n* `6g-5-5975-20` - 5 (5975/20 MHz)\n* `6g-7-5985-80` - 7 (5985/80 MHz)\n* `6g-9-5995-20` - 9 (5995/20 MHz)\n* `6g-11-6005-40` - 11 (6005/40 MHz)\n* `6g-13-6015-20` - 13 (6015/20 MHz)\n* `6g-15-6025-160` - 15 (6025/160 MHz)\n* `6g-17-6035-20` - 17 (6035/20 MHz)\n* `6g-19-6045-40` - 19 (6045/40 MHz)\n* `6g-21-6055-20` - 21 (6055/20 MHz)\n* `6g-23-6065-80` - 23 (6065/80 MHz)\n* `6g-25-6075-20` - 25 (6075/20 MHz)\n* `6g-27-6085-40` - 27 (6085/40 MHz)\n* `6g-29-6095-20` - 29 (6095/20 MHz)\n* `6g-31-6105-320` - 31 (6105/320 MHz)\n* `6g-33-6115-20` - 33 (6115/20 MHz)\n* `6g-35-6125-40` - 35 (6125/40 MHz)\n* `6g-37-6135-20` - 37 (6135/20 MHz)\n* `6g-39-6145-80` - 39 (6145/80 MHz)\n* `6g-41-6155-20` - 41 (6155/20 MHz)\n* `6g-43-6165-40` - 43 (6165/40 MHz)\n* `6g-45-6175-20` - 45 (6175/20 MHz)\n* `6g-47-6185-160` - 47 (6185/160 MHz)\n* `6g-49-6195-20` - 49 (6195/20 MHz)\n* `6g-51-6205-40` - 51 (6205/40 MHz)\n* `6g-53-6215-20` - 53 (6215/20 MHz)\n* `6g-55-6225-80` - 55 (6225/80 MHz)\n* `6g-57-6235-20` - 57 (6235/20 MHz)\n* `6g-59-6245-40` - 59 (6245/40 MHz)\n* `6g-61-6255-20` - 61 (6255/20 MHz)\n* `6g-65-6275-20` - 65 (6275/20 MHz)\n* `6g-67-6285-40` - 67 (6285/40 MHz)\n* `6g-69-6295-20` - 69 (6295/20 MHz)\n* `6g-71-6305-80` - 71 (6305/80 MHz)\n* `6g-73-6315-20` - 73 (6315/20 MHz)\n* `6g-75-6325-40` - 75 (6325/40 MHz)\n* `6g-77-6335-20` - 77 (6335/20 MHz)\n* `6g-79-6345-160` - 79 (6345/160 MHz)\n* `6g-81-6355-20` - 81 (6355/20 MHz)\n* `6g-83-6365-40` - 83 (6365/40 MHz)\n* `6g-85-6375-20` - 85 (6375/20 MHz)\n* `6g-87-6385-80` - 87 (6385/80 MHz)\n* `6g-89-6395-20` - 89 (6395/20 MHz)\n* `6g-91-6405-40` - 91 (6405/40 MHz)\n* `6g-93-6415-20` - 93 (6415/20 MHz)\n* `6g-95-6425-320` - 95 (6425/320 MHz)\n* `6g-97-6435-20` - 97 (6435/20 MHz)\n* `6g-99-6445-40` - 99 (6445/40 MHz)\n* `6g-101-6455-20` - 101 (6455/20 MHz)\n* `6g-103-6465-80` - 103 (6465/80 MHz)\n* `6g-105-6475-20` - 105 (6475/20 MHz)\n* `6g-107-6485-40` - 107 (6485/40 MHz)\n* `6g-109-6495-20` - 109 (6495/20 MHz)\n* `6g-111-6505-160` - 111 (6505/160 MHz)\n* `6g-113-6515-20` - 113 (6515/20 MHz)\n* `6g-115-6525-40` - 115 (6525/40 MHz)\n* `6g-117-6535-20` - 117 (6535/20 MHz)\n* `6g-119-6545-80` - 119 (6545/80 MHz)\n* `6g-121-6555-20` - 121 (6555/20 MHz)\n* `6g-123-6565-40` - 123 (6565/40 MHz)\n* `6g-125-6575-20` - 125 (6575/20 MHz)\n* `6g-129-6595-20` - 129 (6595/20 MHz)\n* `6g-131-6605-40` - 131 (6605/40 MHz)\n* `6g-133-6615-20` - 133 (6615/20 MHz)\n* `6g-135-6625-80` - 135 (6625/80 MHz)\n* `6g-137-6635-20` - 137 (6635/20 MHz)\n* `6g-139-6645-40` - 139 (6645/40 MHz)\n* `6g-141-6655-20` - 141 (6655/20 MHz)\n* `6g-143-6665-160` - 143 (6665/160 MHz)\n* `6g-145-6675-20` - 145 (6675/20 MHz)\n* `6g-147-6685-40` - 147 (6685/40 MHz)\n* `6g-149-6695-20` - 149 (6695/20 MHz)\n* `6g-151-6705-80` - 151 (6705/80 MHz)\n* `6g-153-6715-20` - 153 (6715/20 MHz)\n* `6g-155-6725-40` - 155 (6725/40 MHz)\n* `6g-157-6735-20` - 157 (6735/20 MHz)\n* `6g-159-6745-320` - 159 (6745/320 MHz)\n* `6g-161-6755-20` - 161 (6755/20 MHz)\n* `6g-163-6765-40` - 163 (6765/40 MHz)\n* `6g-165-6775-20` - 165 (6775/20 MHz)\n* `6g-167-6785-80` - 167 (6785/80 MHz)\n* `6g-169-6795-20` - 169 (6795/20 MHz)\n* `6g-171-6805-40` - 171 (6805/40 MHz)\n* `6g-173-6815-20` - 173 (6815/20 MHz)\n* `6g-175-6825-160` - 175 (6825/160 MHz)\n* `6g-177-6835-20` - 177 (6835/20 MHz)\n* `6g-179-6845-40` - 179 (6845/40 MHz)\n* `6g-181-6855-20` - 181 (6855/20 MHz)\n* `6g-183-6865-80` - 183 (6865/80 MHz)\n* `6g-185-6875-20` - 185 (6875/20 MHz)\n* `6g-187-6885-40` - 187 (6885/40 MHz)\n* `6g-189-6895-20` - 189 (6895/20 MHz)\n* `6g-193-6915-20` - 193 (6915/20 MHz)\n* `6g-195-6925-40` - 195 (6925/40 MHz)\n* `6g-197-6935-20` - 197 (6935/20 MHz)\n* `6g-199-6945-80` - 199 (6945/80 MHz)\n* `6g-201-6955-20` - 201 (6955/20 MHz)\n* `6g-203-6965-40` - 203 (6965/40 MHz)\n* `6g-205-6975-20` - 205 (6975/20 MHz)\n* `6g-207-6985-160` - 207 (6985/160 MHz)\n* `6g-209-6995-20` - 209 (6995/20 MHz)\n* `6g-211-7005-40` - 211 (7005/40 MHz)\n* `6g-213-7015-20` - 213 (7015/20 MHz)\n* `6g-215-7025-80` - 215 (7025/80 MHz)\n* `6g-217-7035-20` - 217 (7035/20 MHz)\n* `6g-219-7045-40` - 219 (7045/40 MHz)\n* `6g-221-7055-20` - 221 (7055/20 MHz)\n* `6g-225-7075-20` - 225 (7075/20 MHz)\n* `6g-227-7085-40` - 227 (7085/40 MHz)\n* `6g-229-7095-20` - 229 (7095/20 MHz)\n* `6g-233-7115-20` - 233 (7115/20 MHz)\n* `60g-1-58320-2160` - 1 (58.32/2.16 GHz)\n* `60g-2-60480-2160` - 2 (60.48/2.16 GHz)\n* `60g-3-62640-2160` - 3 (62.64/2.16 GHz)\n* `60g-4-64800-2160` - 4 (64.80/2.16 GHz)\n* `60g-5-66960-2160` - 5 (66.96/2.16 GHz)\n* `60g-6-69120-2160` - 6 (69.12/2.16 GHz)\n* `60g-9-59400-4320` - 9 (59.40/4.32 GHz)\n* `60g-10-61560-4320` - 10 (61.56/4.32 GHz)\n* `60g-11-63720-4320` - 11 (63.72/4.32 GHz)\n* `60g-12-65880-4320` - 12 (65.88/4.32 GHz)\n* `60g-13-68040-4320` - 13 (68.04/4.32 GHz)\n* `60g-17-60480-6480` - 17 (60.48/6.48 GHz)\n* `60g-18-62640-6480` - 18 (62.64/6.48 GHz)\n* `60g-19-64800-6480` - 19 (64.80/6.48 GHz)\n* `60g-20-66960-6480` - 20 (66.96/6.48 GHz)\n* `60g-25-61560-6480` - 25 (61.56/8.64 GHz)\n* `60g-26-63720-6480` - 26 (63.72/8.64 GHz)\n* `60g-27-65880-6480` - 27 (65.88/8.64 GHz)", + "x-spec-enum-id": "70cf66176c475063" + }, + "poe_mode": { + "enum": [ + "pd", + "pse", + "" + ], + "type": "string", + "description": "* `pd` - PD\n* `pse` - PSE", + "x-spec-enum-id": "2f2fe6dcdc7772bd" + }, + "poe_type": { + "enum": [ + "type1-ieee802.3af", + "type2-ieee802.3at", + "type3-ieee802.3bt", + "type4-ieee802.3bt", + "passive-24v-2pair", + "passive-24v-4pair", + "passive-48v-2pair", + "passive-48v-4pair", + "" + ], + "type": "string", + "description": "* `type1-ieee802.3af` - 802.3af (Type 1)\n* `type2-ieee802.3at` - 802.3at (Type 2)\n* `type3-ieee802.3bt` - 802.3bt (Type 3)\n* `type4-ieee802.3bt` - 802.3bt (Type 4)\n* `passive-24v-2pair` - Passive 24V (2-pair)\n* `passive-24v-4pair` - Passive 24V (4-pair)\n* `passive-48v-2pair` - Passive 48V (2-pair)\n* `passive-48v-4pair` - Passive 48V (4-pair)", + "x-spec-enum-id": "5473d57885f237ab" + }, + "rf_channel_frequency": { + "type": "number", + "format": "double", + "maximum": 100000, + "minimum": -100000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Channel frequency (MHz)", + "description": "Populated by selected channel (if set)" + }, + "rf_channel_width": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": -10000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true, + "title": "Channel width (MHz)", + "description": "Populated by selected channel (if set)" + }, + "tx_power": { + "type": "integer", + "maximum": 127, + "minimum": -40, + "nullable": true, + "title": "Transmit power (dBm)" + }, + "untagged_vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tagged_vlans": { + "type": "array", + "items": { + "type": "integer" + } + }, + "qinq_svlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vlan_translation_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANTranslationPolicyRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "wireless_lans": { + "type": "array", + "items": { + "type": "integer" + } + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkInterfaceTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "virtual", + "bridge", + "lag", + "100base-fx", + "100base-lfx", + "100base-tx", + "100base-t1", + "1000base-bx10-d", + "1000base-bx10-u", + "1000base-cwdm", + "1000base-cx", + "1000base-dwdm", + "1000base-ex", + "1000base-lsx", + "1000base-lx", + "1000base-lx10", + "1000base-sx", + "1000base-t", + "1000base-tx", + "1000base-zx", + "2.5gbase-t", + "5gbase-t", + "10gbase-br-d", + "10gbase-br-u", + "10gbase-cu", + "10gbase-cx4", + "10gbase-er", + "10gbase-lr", + "10gbase-lrm", + "10gbase-lx4", + "10gbase-sr", + "10gbase-t", + "10gbase-zr", + "25gbase-cr", + "25gbase-er", + "25gbase-lr", + "25gbase-sr", + "25gbase-t", + "40gbase-cr4", + "40gbase-er4", + "40gbase-fr4", + "40gbase-lr4", + "40gbase-sr4", + "40gbase-sr4-bd", + "50gbase-cr", + "50gbase-er", + "50gbase-fr", + "50gbase-lr", + "50gbase-sr", + "100gbase-cr1", + "100gbase-cr2", + "100gbase-cr4", + "100gbase-cr10", + "100gbase-cwdm4", + "100gbase-dr", + "100gbase-er4", + "100gbase-fr1", + "100gbase-lr1", + "100gbase-lr4", + "100gbase-sr1", + "100gbase-sr1.2", + "100gbase-sr2", + "100gbase-sr4", + "100gbase-sr10", + "100gbase-zr", + "200gbase-cr2", + "200gbase-cr4", + "200gbase-dr4", + "200gbase-er4", + "200gbase-fr4", + "200gbase-lr4", + "200gbase-sr2", + "200gbase-sr4", + "200gbase-vr2", + "400gbase-cr4", + "400gbase-dr4", + "400gbase-er8", + "400gbase-fr4", + "400gbase-fr8", + "400gbase-lr4", + "400gbase-lr8", + "400gbase-sr4", + "400gbase-sr4_2", + "400gbase-sr8", + "400gbase-sr16", + "400gbase-vr4", + "400gbase-zr", + "800gbase-cr8", + "800gbase-dr8", + "800gbase-sr8", + "800gbase-vr8", + "1.6tbase-cr8", + "1.6tbase-dr8", + "1.6tbase-dr8-2", + "100base-x-sfp", + "1000base-x-gbic", + "1000base-x-sfp", + "2.5gbase-x-sfp", + "10gbase-x-sfpp", + "10gbase-x-xenpak", + "10gbase-x-xfp", + "10gbase-x-x2", + "25gbase-x-sfp28", + "40gbase-x-qsfpp", + "50gbase-x-sfp28", + "50gbase-x-sfp56", + "100gbase-x-cfp", + "100gbase-x-cfp2", + "100gbase-x-cfp4", + "100gbase-x-cxp", + "100gbase-x-cpak", + "100gbase-x-dsfp", + "100gbase-x-qsfp28", + "100gbase-x-qsfpdd", + "100gbase-x-sfpdd", + "200gbase-x-cfp2", + "200gbase-x-qsfp56", + "200gbase-x-qsfpdd", + "400gbase-x-qsfp112", + "400gbase-x-qsfpdd", + "400gbase-x-cdfp", + "400gbase-x-cfp2", + "400gbase-x-cfp8", + "400gbase-x-osfp", + "400gbase-x-osfp-rhs", + "800gbase-x-osfp", + "800gbase-x-qsfpdd", + "1.6tbase-x-osfp1600", + "1.6tbase-x-osfp1600-rhs", + "1.6tbase-x-qsfpdd1600", + "1000base-kx", + "2.5gbase-kx", + "5gbase-kr", + "10gbase-kr", + "10gbase-kx4", + "25gbase-kr", + "40gbase-kr4", + "50gbase-kr", + "100gbase-kp4", + "100gbase-kr2", + "100gbase-kr4", + "1.6tbase-kr8", + "ieee802.11a", + "ieee802.11g", + "ieee802.11n", + "ieee802.11ac", + "ieee802.11ad", + "ieee802.11ax", + "ieee802.11ay", + "ieee802.11be", + "ieee802.15.1", + "ieee802.15.4", + "other-wireless", + "gsm", + "cdma", + "lte", + "4g", + "5g", + "sonet-oc3", + "sonet-oc12", + "sonet-oc48", + "sonet-oc192", + "sonet-oc768", + "sonet-oc1920", + "sonet-oc3840", + "1gfc-sfp", + "2gfc-sfp", + "4gfc-sfp", + "8gfc-sfpp", + "16gfc-sfpp", + "32gfc-sfp28", + "32gfc-sfpp", + "64gfc-qsfpp", + "64gfc-sfpdd", + "64gfc-sfpp", + "128gfc-qsfp28", + "infiniband-sdr", + "infiniband-ddr", + "infiniband-qdr", + "infiniband-fdr10", + "infiniband-fdr", + "infiniband-edr", + "infiniband-hdr", + "infiniband-ndr", + "infiniband-xdr", + "t1", + "e1", + "t3", + "e3", + "xdsl", + "docsis", + "moca", + "bpon", + "epon", + "10g-epon", + "gpon", + "xg-pon", + "xgs-pon", + "ng-pon2", + "25g-pon", + "50g-pon", + "cisco-stackwise", + "cisco-stackwise-plus", + "cisco-flexstack", + "cisco-flexstack-plus", + "cisco-stackwise-80", + "cisco-stackwise-160", + "cisco-stackwise-320", + "cisco-stackwise-480", + "cisco-stackwise-1t", + "juniper-vcp", + "extreme-summitstack", + "extreme-summitstack-128", + "extreme-summitstack-256", + "extreme-summitstack-512", + "other" + ], + "type": "string", + "description": "* `virtual` - Virtual\n* `bridge` - Bridge\n* `lag` - Link Aggregation Group (LAG)\n* `100base-fx` - 100BASE-FX (10/100ME)\n* `100base-lfx` - 100BASE-LFX (10/100ME)\n* `100base-tx` - 100BASE-TX (10/100ME)\n* `100base-t1` - 100BASE-T1 (10/100ME)\n* `1000base-bx10-d` - 1000BASE-BX10-D (1GE BiDi Down)\n* `1000base-bx10-u` - 1000BASE-BX10-U (1GE BiDi Up)\n* `1000base-cwdm` - 1000BASE-CWDM (1GE)\n* `1000base-cx` - 1000BASE-CX (1GE DAC)\n* `1000base-dwdm` - 1000BASE-DWDM (1GE)\n* `1000base-ex` - 1000BASE-EX (1GE)\n* `1000base-lsx` - 1000BASE-LSX (1GE)\n* `1000base-lx` - 1000BASE-LX (1GE)\n* `1000base-lx10` - 1000BASE-LX10/LH (1GE)\n* `1000base-sx` - 1000BASE-SX (1GE)\n* `1000base-t` - 1000BASE-T (1GE)\n* `1000base-tx` - 1000BASE-TX (1GE)\n* `1000base-zx` - 1000BASE-ZX (1GE)\n* `2.5gbase-t` - 2.5GBASE-T (2.5GE)\n* `5gbase-t` - 5GBASE-T (5GE)\n* `10gbase-br-d` - 10GBASE-BR-D (10GE BiDi Down)\n* `10gbase-br-u` - 10GBASE-BR-U (10GE BiDi Up)\n* `10gbase-cu` - 10GBASE-CU (10GE DAC Passive Twinax)\n* `10gbase-cx4` - 10GBASE-CX4 (10GE DAC)\n* `10gbase-er` - 10GBASE-ER (10GE)\n* `10gbase-lr` - 10GBASE-LR (10GE)\n* `10gbase-lrm` - 10GBASE-LRM (10GE)\n* `10gbase-lx4` - 10GBASE-LX4 (10GE)\n* `10gbase-sr` - 10GBASE-SR (10GE)\n* `10gbase-t` - 10GBASE-T (10GE)\n* `10gbase-zr` - 10GBASE-ZR (10GE)\n* `25gbase-cr` - 25GBASE-CR (25GE DAC)\n* `25gbase-er` - 25GBASE-ER (25GE)\n* `25gbase-lr` - 25GBASE-LR (25GE)\n* `25gbase-sr` - 25GBASE-SR (25GE)\n* `25gbase-t` - 25GBASE-T (25GE)\n* `40gbase-cr4` - 40GBASE-CR4 (40GE DAC)\n* `40gbase-er4` - 40GBASE-ER4 (40GE)\n* `40gbase-fr4` - 40GBASE-FR4 (40GE)\n* `40gbase-lr4` - 40GBASE-LR4 (40GE)\n* `40gbase-sr4` - 40GBASE-SR4 (40GE)\n* `40gbase-sr4-bd` - 40GBASE-SR4 (40GE BiDi)\n* `50gbase-cr` - 50GBASE-CR (50GE DAC)\n* `50gbase-er` - 50GBASE-ER (50GE)\n* `50gbase-fr` - 50GBASE-FR (50GE)\n* `50gbase-lr` - 50GBASE-LR (50GE)\n* `50gbase-sr` - 50GBASE-SR (50GE)\n* `100gbase-cr1` - 100GBASE-CR1 (100GE DAC)\n* `100gbase-cr2` - 100GBASE-CR2 (100GE DAC)\n* `100gbase-cr4` - 100GBASE-CR4 (100GE DAC)\n* `100gbase-cr10` - 100GBASE-CR10 (100GE DAC)\n* `100gbase-cwdm4` - 100GBASE-CWDM4 (100GE)\n* `100gbase-dr` - 100GBASE-DR (100GE)\n* `100gbase-er4` - 100GBASE-ER4 (100GE)\n* `100gbase-fr1` - 100GBASE-FR1 (100GE)\n* `100gbase-lr1` - 100GBASE-LR1 (100GE)\n* `100gbase-lr4` - 100GBASE-LR4 (100GE)\n* `100gbase-sr1` - 100GBASE-SR1 (100GE)\n* `100gbase-sr1.2` - 100GBASE-SR1.2 (100GE BiDi)\n* `100gbase-sr2` - 100GBASE-SR2 (100GE)\n* `100gbase-sr4` - 100GBASE-SR4 (100GE)\n* `100gbase-sr10` - 100GBASE-SR10 (100GE)\n* `100gbase-zr` - 100GBASE-ZR (100GE)\n* `200gbase-cr2` - 200GBASE-CR2 (200GE)\n* `200gbase-cr4` - 200GBASE-CR4 (200GE)\n* `200gbase-dr4` - 200GBASE-DR4 (200GE)\n* `200gbase-er4` - 200GBASE-ER4 (200GE)\n* `200gbase-fr4` - 200GBASE-FR4 (200GE)\n* `200gbase-lr4` - 200GBASE-LR4 (200GE)\n* `200gbase-sr2` - 200GBASE-SR2 (200GE)\n* `200gbase-sr4` - 200GBASE-SR4 (200GE)\n* `200gbase-vr2` - 200GBASE-VR2 (200GE)\n* `400gbase-cr4` - 400GBASE-CR4 (400GE)\n* `400gbase-dr4` - 400GBASE-DR4 (400GE)\n* `400gbase-er8` - 400GBASE-ER8 (400GE)\n* `400gbase-fr4` - 400GBASE-FR4 (400GE)\n* `400gbase-fr8` - 400GBASE-FR8 (400GE)\n* `400gbase-lr4` - 400GBASE-LR4 (400GE)\n* `400gbase-lr8` - 400GBASE-LR8 (400GE)\n* `400gbase-sr4` - 400GBASE-SR4 (400GE)\n* `400gbase-sr4_2` - 400GBASE-SR4.2 (400GE BiDi)\n* `400gbase-sr8` - 400GBASE-SR8 (400GE)\n* `400gbase-sr16` - 400GBASE-SR16 (400GE)\n* `400gbase-vr4` - 400GBASE-VR4 (400GE)\n* `400gbase-zr` - 400GBASE-ZR (400GE)\n* `800gbase-cr8` - 800GBASE-CR8 (800GE)\n* `800gbase-dr8` - 800GBASE-DR8 (800GE)\n* `800gbase-sr8` - 800GBASE-SR8 (800GE)\n* `800gbase-vr8` - 800GBASE-VR8 (800GE)\n* `1.6tbase-cr8` - 1.6TBASE-CR8 (1.6TE)\n* `1.6tbase-dr8` - 1.6TBASE-DR8 (1.6TE)\n* `1.6tbase-dr8-2` - 1.6TBASE-DR8-2 (1.6TE)\n* `100base-x-sfp` - SFP (100ME)\n* `1000base-x-gbic` - GBIC (1GE)\n* `1000base-x-sfp` - SFP (1GE)\n* `2.5gbase-x-sfp` - SFP (2.5GE)\n* `10gbase-x-sfpp` - SFP+ (10GE)\n* `10gbase-x-xenpak` - XENPAK (10GE)\n* `10gbase-x-xfp` - XFP (10GE)\n* `10gbase-x-x2` - X2 (10GE)\n* `25gbase-x-sfp28` - SFP28 (25GE)\n* `40gbase-x-qsfpp` - QSFP+ (40GE)\n* `50gbase-x-sfp28` - QSFP28 (50GE)\n* `50gbase-x-sfp56` - SFP56 (50GE)\n* `100gbase-x-cfp` - CFP (100GE)\n* `100gbase-x-cfp2` - CFP2 (100GE)\n* `100gbase-x-cfp4` - CFP4 (100GE)\n* `100gbase-x-cxp` - CXP (100GE)\n* `100gbase-x-cpak` - Cisco CPAK (100GE)\n* `100gbase-x-dsfp` - DSFP (100GE)\n* `100gbase-x-qsfp28` - QSFP28 (100GE)\n* `100gbase-x-qsfpdd` - QSFP-DD (100GE)\n* `100gbase-x-sfpdd` - SFP-DD (100GE)\n* `200gbase-x-cfp2` - CFP2 (200GE)\n* `200gbase-x-qsfp56` - QSFP56 (200GE)\n* `200gbase-x-qsfpdd` - QSFP-DD (200GE)\n* `400gbase-x-qsfp112` - QSFP112 (400GE)\n* `400gbase-x-qsfpdd` - QSFP-DD (400GE)\n* `400gbase-x-cdfp` - CDFP (400GE)\n* `400gbase-x-cfp2` - CFP2 (400GE)\n* `400gbase-x-cfp8` - CPF8 (400GE)\n* `400gbase-x-osfp` - OSFP (400GE)\n* `400gbase-x-osfp-rhs` - OSFP-RHS (400GE)\n* `800gbase-x-osfp` - OSFP (800GE)\n* `800gbase-x-qsfpdd` - QSFP-DD (800GE)\n* `1.6tbase-x-osfp1600` - OSFP1600 (1.6TE)\n* `1.6tbase-x-osfp1600-rhs` - OSFP1600-RHS (1.6TE)\n* `1.6tbase-x-qsfpdd1600` - QSFP-DD1600 (1.6TE)\n* `1000base-kx` - 1000BASE-KX (1GE)\n* `2.5gbase-kx` - 2.5GBASE-KX (2.5GE)\n* `5gbase-kr` - 5GBASE-KR (5GE)\n* `10gbase-kr` - 10GBASE-KR (10GE)\n* `10gbase-kx4` - 10GBASE-KX4 (10GE)\n* `25gbase-kr` - 25GBASE-KR (25GE)\n* `40gbase-kr4` - 40GBASE-KR4 (40GE)\n* `50gbase-kr` - 50GBASE-KR (50GE)\n* `100gbase-kp4` - 100GBASE-KP4 (100GE)\n* `100gbase-kr2` - 100GBASE-KR2 (100GE)\n* `100gbase-kr4` - 100GBASE-KR4 (100GE)\n* `1.6tbase-kr8` - 1.6TBASE-KR8 (1.6TE)\n* `ieee802.11a` - IEEE 802.11a\n* `ieee802.11g` - IEEE 802.11b/g\n* `ieee802.11n` - IEEE 802.11n (Wi-Fi 4)\n* `ieee802.11ac` - IEEE 802.11ac (Wi-Fi 5)\n* `ieee802.11ad` - IEEE 802.11ad (WiGig)\n* `ieee802.11ax` - IEEE 802.11ax (Wi-Fi 6)\n* `ieee802.11ay` - IEEE 802.11ay (WiGig)\n* `ieee802.11be` - IEEE 802.11be (Wi-Fi 7)\n* `ieee802.15.1` - IEEE 802.15.1 (Bluetooth)\n* `ieee802.15.4` - IEEE 802.15.4 (LR-WPAN)\n* `other-wireless` - Other (Wireless)\n* `gsm` - GSM\n* `cdma` - CDMA\n* `lte` - LTE\n* `4g` - 4G\n* `5g` - 5G\n* `sonet-oc3` - OC-3/STM-1\n* `sonet-oc12` - OC-12/STM-4\n* `sonet-oc48` - OC-48/STM-16\n* `sonet-oc192` - OC-192/STM-64\n* `sonet-oc768` - OC-768/STM-256\n* `sonet-oc1920` - OC-1920/STM-640\n* `sonet-oc3840` - OC-3840/STM-1234\n* `1gfc-sfp` - SFP (1GFC)\n* `2gfc-sfp` - SFP (2GFC)\n* `4gfc-sfp` - SFP (4GFC)\n* `8gfc-sfpp` - SFP+ (8GFC)\n* `16gfc-sfpp` - SFP+ (16GFC)\n* `32gfc-sfp28` - SFP28 (32GFC)\n* `32gfc-sfpp` - SFP+ (32GFC)\n* `64gfc-qsfpp` - QSFP+ (64GFC)\n* `64gfc-sfpdd` - SFP-DD (64GFC)\n* `64gfc-sfpp` - SFP+ (64GFC)\n* `128gfc-qsfp28` - QSFP28 (128GFC)\n* `infiniband-sdr` - SDR (2 Gbps)\n* `infiniband-ddr` - DDR (4 Gbps)\n* `infiniband-qdr` - QDR (8 Gbps)\n* `infiniband-fdr10` - FDR10 (10 Gbps)\n* `infiniband-fdr` - FDR (13.5 Gbps)\n* `infiniband-edr` - EDR (25 Gbps)\n* `infiniband-hdr` - HDR (50 Gbps)\n* `infiniband-ndr` - NDR (100 Gbps)\n* `infiniband-xdr` - XDR (250 Gbps)\n* `t1` - T1 (1.544 Mbps)\n* `e1` - E1 (2.048 Mbps)\n* `t3` - T3 (45 Mbps)\n* `e3` - E3 (34 Mbps)\n* `xdsl` - xDSL\n* `docsis` - DOCSIS\n* `moca` - MoCA\n* `bpon` - BPON (622 Mbps / 155 Mbps)\n* `epon` - EPON (1 Gbps)\n* `10g-epon` - 10G-EPON (10 Gbps)\n* `gpon` - GPON (2.5 Gbps / 1.25 Gbps)\n* `xg-pon` - XG-PON (10 Gbps / 2.5 Gbps)\n* `xgs-pon` - XGS-PON (10 Gbps)\n* `ng-pon2` - NG-PON2 (TWDM-PON) (4x10 Gbps)\n* `25g-pon` - 25G-PON (25 Gbps)\n* `50g-pon` - 50G-PON (50 Gbps)\n* `cisco-stackwise` - Cisco StackWise\n* `cisco-stackwise-plus` - Cisco StackWise Plus\n* `cisco-flexstack` - Cisco FlexStack\n* `cisco-flexstack-plus` - Cisco FlexStack Plus\n* `cisco-stackwise-80` - Cisco StackWise-80\n* `cisco-stackwise-160` - Cisco StackWise-160\n* `cisco-stackwise-320` - Cisco StackWise-320\n* `cisco-stackwise-480` - Cisco StackWise-480\n* `cisco-stackwise-1t` - Cisco StackWise-1T\n* `juniper-vcp` - Juniper VCP\n* `extreme-summitstack` - Extreme SummitStack\n* `extreme-summitstack-128` - Extreme SummitStack-128\n* `extreme-summitstack-256` - Extreme SummitStack-256\n* `extreme-summitstack-512` - Extreme SummitStack-512\n* `other` - Other", + "x-spec-enum-id": "b067eb1f050c6ae9" + }, + "enabled": { + "type": "boolean" + }, + "mgmt_only": { + "type": "boolean", + "title": "Management only" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "bridge": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedInterfaceTemplateRequest" + } + ], + "nullable": true + }, + "poe_mode": { + "enum": [ + "pd", + "pse", + "", + null + ], + "type": "string", + "description": "* `pd` - PD\n* `pse` - PSE", + "x-spec-enum-id": "2f2fe6dcdc7772bd", + "nullable": true + }, + "poe_type": { + "enum": [ + "type1-ieee802.3af", + "type2-ieee802.3at", + "type3-ieee802.3bt", + "type4-ieee802.3bt", + "passive-24v-2pair", + "passive-24v-4pair", + "passive-48v-2pair", + "passive-48v-4pair", + "", + null + ], + "type": "string", + "description": "* `type1-ieee802.3af` - 802.3af (Type 1)\n* `type2-ieee802.3at` - 802.3at (Type 2)\n* `type3-ieee802.3bt` - 802.3bt (Type 3)\n* `type4-ieee802.3bt` - 802.3bt (Type 4)\n* `passive-24v-2pair` - Passive 24V (2-pair)\n* `passive-24v-4pair` - Passive 24V (4-pair)\n* `passive-48v-2pair` - Passive 48V (2-pair)\n* `passive-48v-4pair` - Passive 48V (4-pair)", + "x-spec-enum-id": "5473d57885f237ab", + "nullable": true + }, + "rf_role": { + "enum": [ + "ap", + "station", + "", + null + ], + "type": "string", + "description": "* `ap` - Access point\n* `station` - Station", + "x-spec-enum-id": "d2772dbea88b0fb1", + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkInventoryItemRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "parent": { + "type": "integer", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "staged", + "failed", + "decommissioning" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "545817eb4c4f2ae4" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefInventoryItemRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "part_id": { + "type": "string", + "description": "Manufacturer-assigned part identifier", + "maxLength": 50 + }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this item", + "maxLength": 50 + }, + "discovered": { + "type": "boolean", + "description": "This item was automatically discovered" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "component_type": { + "type": "string", + "nullable": true + }, + "component_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkInventoryItemRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id" + ] + }, + "PatchedBulkInventoryItemTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ] + }, + "parent": { + "type": "integer", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefInventoryItemRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "part_id": { + "type": "string", + "description": "Manufacturer-assigned part identifier", + "maxLength": 50 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "component_type": { + "type": "string", + "nullable": true + }, + "component_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkJournalEntryRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "assigned_object_type": { + "type": "string" + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "created_by": { + "type": "integer", + "nullable": true + }, + "kind": { + "enum": [ + "info", + "success", + "warning", + "danger" + ], + "type": "string", + "description": "* `info` - Info\n* `success` - Success\n* `warning` - Warning\n* `danger` - Danger", + "x-spec-enum-id": "6f65abe0aab2c78c" + }, + "comments": { + "type": "string", + "minLength": 1 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkL2VPNRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "identifier": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": -9223372036854775808, + "format": "int64", + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "type": { + "enum": [ + "vpws", + "vpls", + "vxlan", + "vxlan-evpn", + "mpls-evpn", + "pbb-evpn", + "evpn-vpws", + "epl", + "evpl", + "ep-lan", + "evp-lan", + "ep-tree", + "evp-tree", + "spb" + ], + "type": "string", + "description": "* `vpws` - VPWS\n* `vpls` - VPLS\n* `vxlan` - VXLAN\n* `vxlan-evpn` - VXLAN-EVPN\n* `mpls-evpn` - MPLS EVPN\n* `pbb-evpn` - PBB EVPN\n* `evpn-vpws` - EVPN VPWS\n* `epl` - EPL\n* `evpl` - EVPL\n* `ep-lan` - Ethernet Private LAN\n* `evp-lan` - Ethernet Virtual Private LAN\n* `ep-tree` - Ethernet Private Tree\n* `evp-tree` - Ethernet Virtual Private Tree\n* `spb` - SPB", + "x-spec-enum-id": "0a46f8056d717efc" + }, + "status": { + "enum": [ + "active", + "planned", + "decommissioning" + ], + "type": "string", + "description": "* `active` - Active\n* `planned` - Planned\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "8b9dc8efc7c3d5b0" + }, + "import_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "export_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkL2VPNTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "l2vpn": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefL2VPNRequest" + } + ] + }, + "assigned_object_type": { + "type": "string" + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkLocationRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedLocationRequest" + } + ], + "nullable": true + }, + "status": { + "enum": [ + "planned", + "staging", + "active", + "decommissioning", + "retired" + ], + "type": "string", + "description": "* `planned` - Planned\n* `staging` - Staging\n* `active` - Active\n* `decommissioning` - Decommissioning\n* `retired` - Retired", + "x-spec-enum-id": "1cf60831fbb35e7f" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "facility": { + "type": "string", + "description": "Local facility ID or description", + "maxLength": 50 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkMACAddressRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "mac_address": { + "type": "string", + "minLength": 1 + }, + "assigned_object_type": { + "type": "string", + "nullable": true + }, + "assigned_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkManufacturerRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkModuleBayRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkModuleBayTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "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 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkModuleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module_bay": { + "$ref": "#/components/schemas/NestedModuleBayRequest" + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ] + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "staged", + "failed", + "decommissioning" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "545817eb4c4f2ae4" + }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this device", + "maxLength": 50 + }, + "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": {} + }, + "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": [ + "id" + ] + }, + "PatchedBulkModuleTypeProfileRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "schema": { + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkModuleTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "profile": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeProfileRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ] + }, + "model": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "part_number": { + "type": "string", + "description": "Discrete part number (optional)", + "maxLength": 50 + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "left-to-right", + "right-to-left", + "side-to-rear", + "passive", + "", + null + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front\n* `left-to-right` - Left to right\n* `right-to-left` - Right to left\n* `side-to-rear` - Side to rear\n* `passive` - Passive", + "x-spec-enum-id": "5ad4e700c656b09d", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "attributes": { + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkNotificationGroupRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkNotificationRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + }, + "read": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "event_type": { + "enum": [ + "object_created", + "object_updated", + "object_deleted", + "job_started", + "job_completed", + "job_failed", + "job_errored" + ], + "type": "string", + "description": "* `object_created` - Object created\n* `object_updated` - Object updated\n* `object_deleted` - Object deleted\n* `job_started` - Job started\n* `job_completed` - Job completed\n* `job_failed` - Job failed\n* `job_errored` - Job errored", + "x-spec-enum-id": "01e557313a5c7bd2", + "title": "Event" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkObjectPermissionRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "enabled": { + "type": "boolean" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "actions": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 30 + }, + "description": "The list of actions granted by this permission" + }, + "constraints": { + "nullable": true, + "description": "Queryset filter matching the applicable objects of the selected type(s)" + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkOwnerGroupRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkOwnerRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "user_groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "users": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkPlatformRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedPlatformRequest" + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "config_template": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkPowerFeedRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "power_panel": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefPowerPanelRequest" + } + ] + }, + "rack": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "status": { + "enum": [ + "offline", + "active", + "planned", + "failed" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `failed` - Failed", + "x-spec-enum-id": "ec530572dc778583" + }, + "type": { + "enum": [ + "primary", + "redundant" + ], + "type": "string", + "description": "* `primary` - Primary\n* `redundant` - Redundant", + "x-spec-enum-id": "093a164236819eb8" + }, + "supply": { + "enum": [ + "ac", + "dc" + ], + "type": "string", + "description": "* `ac` - AC\n* `dc` - DC", + "x-spec-enum-id": "1b6d99616ca6412b" + }, + "phase": { + "enum": [ + "single-phase", + "three-phase" + ], + "type": "string", + "description": "* `single-phase` - Single phase\n* `three-phase` - Three-phase", + "x-spec-enum-id": "994bc0696f4df57f" + }, + "voltage": { + "type": "integer", + "maximum": 32767, + "minimum": -32768 + }, + "amperage": { + "type": "integer", + "maximum": 32767, + "minimum": 1 + }, + "max_utilization": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "description": "Maximum permissible draw (percentage)" + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkPowerOutletRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c17", + "iec-60320-c19", + "iec-60320-c21", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15r", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-10-30r", + "nema-10-50r", + "nema-14-20r", + "nema-14-30r", + "nema-14-50r", + "nema-14-60r", + "nema-15-15r", + "nema-15-20r", + "nema-15-30r", + "nema-15-50r", + "nema-15-60r", + "nema-l1-15r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-15r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "nema-l10-30r", + "nema-l14-20r", + "nema-l14-30r", + "nema-l14-50r", + "nema-l14-60r", + "nema-l15-20r", + "nema-l15-30r", + "nema-l15-50r", + "nema-l15-60r", + "nema-l21-20r", + "nema-l21-30r", + "nema-l22-20r", + "nema-l22-30r", + "CS6360C", + "CS6364C", + "CS8164C", + "CS8264C", + "CS8364C", + "CS8464C", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "ita-multistandard", + "usb-a", + "usb-micro-b", + "usb-c", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "eaton-c39", + "hdot-cx", + "saf-d-grid", + "neutrik-powercon-20a", + "neutrik-powercon-32a", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c5` - C5\n* `iec-60320-c7` - C7\n* `iec-60320-c13` - C13\n* `iec-60320-c15` - C15\n* `iec-60320-c17` - C17\n* `iec-60320-c19` - C19\n* `iec-60320-c21` - C21\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15r` - NEMA 1-15R\n* `nema-5-15r` - NEMA 5-15R\n* `nema-5-20r` - NEMA 5-20R\n* `nema-5-30r` - NEMA 5-30R\n* `nema-5-50r` - NEMA 5-50R\n* `nema-6-15r` - NEMA 6-15R\n* `nema-6-20r` - NEMA 6-20R\n* `nema-6-30r` - NEMA 6-30R\n* `nema-6-50r` - NEMA 6-50R\n* `nema-10-30r` - NEMA 10-30R\n* `nema-10-50r` - NEMA 10-50R\n* `nema-14-20r` - NEMA 14-20R\n* `nema-14-30r` - NEMA 14-30R\n* `nema-14-50r` - NEMA 14-50R\n* `nema-14-60r` - NEMA 14-60R\n* `nema-15-15r` - NEMA 15-15R\n* `nema-15-20r` - NEMA 15-20R\n* `nema-15-30r` - NEMA 15-30R\n* `nema-15-50r` - NEMA 15-50R\n* `nema-15-60r` - NEMA 15-60R\n* `nema-l1-15r` - NEMA L1-15R\n* `nema-l5-15r` - NEMA L5-15R\n* `nema-l5-20r` - NEMA L5-20R\n* `nema-l5-30r` - NEMA L5-30R\n* `nema-l5-50r` - NEMA L5-50R\n* `nema-l6-15r` - NEMA L6-15R\n* `nema-l6-20r` - NEMA L6-20R\n* `nema-l6-30r` - NEMA L6-30R\n* `nema-l6-50r` - NEMA L6-50R\n* `nema-l10-30r` - NEMA L10-30R\n* `nema-l14-20r` - NEMA L14-20R\n* `nema-l14-30r` - NEMA L14-30R\n* `nema-l14-50r` - NEMA L14-50R\n* `nema-l14-60r` - NEMA L14-60R\n* `nema-l15-20r` - NEMA L15-20R\n* `nema-l15-30r` - NEMA L15-30R\n* `nema-l15-50r` - NEMA L15-50R\n* `nema-l15-60r` - NEMA L15-60R\n* `nema-l21-20r` - NEMA L21-20R\n* `nema-l21-30r` - NEMA L21-30R\n* `nema-l22-20r` - NEMA L22-20R\n* `nema-l22-30r` - NEMA L22-30R\n* `CS6360C` - CS6360C\n* `CS6364C` - CS6364C\n* `CS8164C` - CS8164C\n* `CS8264C` - CS8264C\n* `CS8364C` - CS8364C\n* `CS8464C` - CS8464C\n* `ita-e` - ITA Type E (CEE 7/5)\n* `ita-f` - ITA Type F (CEE 7/3)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `ita-multistandard` - ITA Multistandard\n* `usb-a` - USB Type A\n* `usb-micro-b` - USB Micro B\n* `usb-c` - USB Type C\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `eaton-c39` - Eaton C39\n* `hdot-cx` - HDOT Cx\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20a` - Neutrik powerCON (20A)\n* `neutrik-powercon-32a` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "db3e4eb2b93615f8", + "nullable": true + }, + "status": { + "enum": [ + "enabled", + "disabled", + "faulty" + ], + "type": "string", + "description": "* `enabled` - Enabled\n* `disabled` - Disabled\n* `faulty` - Faulty", + "x-spec-enum-id": "d60dce16858f3c69" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "power_port": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPowerPortRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "feed_leg": { + "enum": [ + "A", + "B", + "C", + "", + null + ], + "type": "string", + "description": "* `A` - A\n* `B` - B\n* `C` - C", + "x-spec-enum-id": "a4902339df0b7c06", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkPowerOutletTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c5", + "iec-60320-c7", + "iec-60320-c13", + "iec-60320-c15", + "iec-60320-c17", + "iec-60320-c19", + "iec-60320-c21", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15r", + "nema-5-15r", + "nema-5-20r", + "nema-5-30r", + "nema-5-50r", + "nema-6-15r", + "nema-6-20r", + "nema-6-30r", + "nema-6-50r", + "nema-10-30r", + "nema-10-50r", + "nema-14-20r", + "nema-14-30r", + "nema-14-50r", + "nema-14-60r", + "nema-15-15r", + "nema-15-20r", + "nema-15-30r", + "nema-15-50r", + "nema-15-60r", + "nema-l1-15r", + "nema-l5-15r", + "nema-l5-20r", + "nema-l5-30r", + "nema-l5-50r", + "nema-l6-15r", + "nema-l6-20r", + "nema-l6-30r", + "nema-l6-50r", + "nema-l10-30r", + "nema-l14-20r", + "nema-l14-30r", + "nema-l14-50r", + "nema-l14-60r", + "nema-l15-20r", + "nema-l15-30r", + "nema-l15-50r", + "nema-l15-60r", + "nema-l21-20r", + "nema-l21-30r", + "nema-l22-20r", + "nema-l22-30r", + "CS6360C", + "CS6364C", + "CS8164C", + "CS8264C", + "CS8364C", + "CS8464C", + "ita-e", + "ita-f", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "ita-multistandard", + "usb-a", + "usb-micro-b", + "usb-c", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "eaton-c39", + "hdot-cx", + "saf-d-grid", + "neutrik-powercon-20a", + "neutrik-powercon-32a", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c5` - C5\n* `iec-60320-c7` - C7\n* `iec-60320-c13` - C13\n* `iec-60320-c15` - C15\n* `iec-60320-c17` - C17\n* `iec-60320-c19` - C19\n* `iec-60320-c21` - C21\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15r` - NEMA 1-15R\n* `nema-5-15r` - NEMA 5-15R\n* `nema-5-20r` - NEMA 5-20R\n* `nema-5-30r` - NEMA 5-30R\n* `nema-5-50r` - NEMA 5-50R\n* `nema-6-15r` - NEMA 6-15R\n* `nema-6-20r` - NEMA 6-20R\n* `nema-6-30r` - NEMA 6-30R\n* `nema-6-50r` - NEMA 6-50R\n* `nema-10-30r` - NEMA 10-30R\n* `nema-10-50r` - NEMA 10-50R\n* `nema-14-20r` - NEMA 14-20R\n* `nema-14-30r` - NEMA 14-30R\n* `nema-14-50r` - NEMA 14-50R\n* `nema-14-60r` - NEMA 14-60R\n* `nema-15-15r` - NEMA 15-15R\n* `nema-15-20r` - NEMA 15-20R\n* `nema-15-30r` - NEMA 15-30R\n* `nema-15-50r` - NEMA 15-50R\n* `nema-15-60r` - NEMA 15-60R\n* `nema-l1-15r` - NEMA L1-15R\n* `nema-l5-15r` - NEMA L5-15R\n* `nema-l5-20r` - NEMA L5-20R\n* `nema-l5-30r` - NEMA L5-30R\n* `nema-l5-50r` - NEMA L5-50R\n* `nema-l6-15r` - NEMA L6-15R\n* `nema-l6-20r` - NEMA L6-20R\n* `nema-l6-30r` - NEMA L6-30R\n* `nema-l6-50r` - NEMA L6-50R\n* `nema-l10-30r` - NEMA L10-30R\n* `nema-l14-20r` - NEMA L14-20R\n* `nema-l14-30r` - NEMA L14-30R\n* `nema-l14-50r` - NEMA L14-50R\n* `nema-l14-60r` - NEMA L14-60R\n* `nema-l15-20r` - NEMA L15-20R\n* `nema-l15-30r` - NEMA L15-30R\n* `nema-l15-50r` - NEMA L15-50R\n* `nema-l15-60r` - NEMA L15-60R\n* `nema-l21-20r` - NEMA L21-20R\n* `nema-l21-30r` - NEMA L21-30R\n* `nema-l22-20r` - NEMA L22-20R\n* `nema-l22-30r` - NEMA L22-30R\n* `CS6360C` - CS6360C\n* `CS6364C` - CS6364C\n* `CS8164C` - CS8164C\n* `CS8264C` - CS8264C\n* `CS8364C` - CS8364C\n* `CS8464C` - CS8464C\n* `ita-e` - ITA Type E (CEE 7/5)\n* `ita-f` - ITA Type F (CEE 7/3)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `ita-multistandard` - ITA Multistandard\n* `usb-a` - USB Type A\n* `usb-micro-b` - USB Micro B\n* `usb-c` - USB Type C\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `eaton-c39` - Eaton C39\n* `hdot-cx` - HDOT Cx\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20a` - Neutrik powerCON (20A)\n* `neutrik-powercon-32a` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "db3e4eb2b93615f8", + "nullable": true + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "power_port": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPowerPortTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "feed_leg": { + "enum": [ + "A", + "B", + "C", + "", + null + ], + "type": "string", + "description": "* `A` - A\n* `B` - B\n* `C` - C", + "x-spec-enum-id": "a4902339df0b7c06", + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkPowerPanelRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "location": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefLocationRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkPowerPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c18", + "iec-60320-c20", + "iec-60320-c22", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15p", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-10-30p", + "nema-10-50p", + "nema-14-20p", + "nema-14-30p", + "nema-14-50p", + "nema-14-60p", + "nema-15-15p", + "nema-15-20p", + "nema-15-30p", + "nema-15-50p", + "nema-15-60p", + "nema-l1-15p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-15p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "nema-l10-30p", + "nema-l14-20p", + "nema-l14-30p", + "nema-l14-50p", + "nema-l14-60p", + "nema-l15-20p", + "nema-l15-30p", + "nema-l15-50p", + "nema-l15-60p", + "nema-l21-20p", + "nema-l21-30p", + "nema-l22-20p", + "nema-l22-30p", + "cs6361c", + "cs6365c", + "cs8165c", + "cs8265c", + "cs8365c", + "cs8465c", + "ita-c", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "usb-3-b", + "usb-3-micro-b", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "saf-d-grid", + "neutrik-powercon-20", + "neutrik-powercon-32", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c6` - C6\n* `iec-60320-c8` - C8\n* `iec-60320-c14` - C14\n* `iec-60320-c16` - C16\n* `iec-60320-c18` - C18\n* `iec-60320-c20` - C20\n* `iec-60320-c22` - C22\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15p` - NEMA 1-15P\n* `nema-5-15p` - NEMA 5-15P\n* `nema-5-20p` - NEMA 5-20P\n* `nema-5-30p` - NEMA 5-30P\n* `nema-5-50p` - NEMA 5-50P\n* `nema-6-15p` - NEMA 6-15P\n* `nema-6-20p` - NEMA 6-20P\n* `nema-6-30p` - NEMA 6-30P\n* `nema-6-50p` - NEMA 6-50P\n* `nema-10-30p` - NEMA 10-30P\n* `nema-10-50p` - NEMA 10-50P\n* `nema-14-20p` - NEMA 14-20P\n* `nema-14-30p` - NEMA 14-30P\n* `nema-14-50p` - NEMA 14-50P\n* `nema-14-60p` - NEMA 14-60P\n* `nema-15-15p` - NEMA 15-15P\n* `nema-15-20p` - NEMA 15-20P\n* `nema-15-30p` - NEMA 15-30P\n* `nema-15-50p` - NEMA 15-50P\n* `nema-15-60p` - NEMA 15-60P\n* `nema-l1-15p` - NEMA L1-15P\n* `nema-l5-15p` - NEMA L5-15P\n* `nema-l5-20p` - NEMA L5-20P\n* `nema-l5-30p` - NEMA L5-30P\n* `nema-l5-50p` - NEMA L5-50P\n* `nema-l6-15p` - NEMA L6-15P\n* `nema-l6-20p` - NEMA L6-20P\n* `nema-l6-30p` - NEMA L6-30P\n* `nema-l6-50p` - NEMA L6-50P\n* `nema-l10-30p` - NEMA L10-30P\n* `nema-l14-20p` - NEMA L14-20P\n* `nema-l14-30p` - NEMA L14-30P\n* `nema-l14-50p` - NEMA L14-50P\n* `nema-l14-60p` - NEMA L14-60P\n* `nema-l15-20p` - NEMA L15-20P\n* `nema-l15-30p` - NEMA L15-30P\n* `nema-l15-50p` - NEMA L15-50P\n* `nema-l15-60p` - NEMA L15-60P\n* `nema-l21-20p` - NEMA L21-20P\n* `nema-l21-30p` - NEMA L21-30P\n* `nema-l22-20p` - NEMA L22-20P\n* `nema-l22-30p` - NEMA L22-30P\n* `cs6361c` - CS6361C\n* `cs6365c` - CS6365C\n* `cs8165c` - CS8165C\n* `cs8265c` - CS8265C\n* `cs8365c` - CS8365C\n* `cs8465c` - CS8465C\n* `ita-c` - ITA Type C (CEE 7/16)\n* `ita-e` - ITA Type E (CEE 7/6)\n* `ita-f` - ITA Type F (CEE 7/4)\n* `ita-ef` - ITA Type E/F (CEE 7/7)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `usb-3-b` - USB 3.0 Type B\n* `usb-3-micro-b` - USB 3.0 Micro B\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20` - Neutrik powerCON (20A)\n* `neutrik-powercon-32` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "aadcbe6ca854c1ed", + "nullable": true + }, + "maximum_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Maximum power draw (watts)" + }, + "allocated_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Allocated power draw (watts)" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkPowerPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "iec-60320-c6", + "iec-60320-c8", + "iec-60320-c14", + "iec-60320-c16", + "iec-60320-c18", + "iec-60320-c20", + "iec-60320-c22", + "iec-60309-p-n-e-4h", + "iec-60309-p-n-e-6h", + "iec-60309-p-n-e-9h", + "iec-60309-2p-e-4h", + "iec-60309-2p-e-6h", + "iec-60309-2p-e-9h", + "iec-60309-3p-e-4h", + "iec-60309-3p-e-6h", + "iec-60309-3p-e-9h", + "iec-60309-3p-n-e-4h", + "iec-60309-3p-n-e-6h", + "iec-60309-3p-n-e-9h", + "iec-60906-1", + "nbr-14136-10a", + "nbr-14136-20a", + "nema-1-15p", + "nema-5-15p", + "nema-5-20p", + "nema-5-30p", + "nema-5-50p", + "nema-6-15p", + "nema-6-20p", + "nema-6-30p", + "nema-6-50p", + "nema-10-30p", + "nema-10-50p", + "nema-14-20p", + "nema-14-30p", + "nema-14-50p", + "nema-14-60p", + "nema-15-15p", + "nema-15-20p", + "nema-15-30p", + "nema-15-50p", + "nema-15-60p", + "nema-l1-15p", + "nema-l5-15p", + "nema-l5-20p", + "nema-l5-30p", + "nema-l5-50p", + "nema-l6-15p", + "nema-l6-20p", + "nema-l6-30p", + "nema-l6-50p", + "nema-l10-30p", + "nema-l14-20p", + "nema-l14-30p", + "nema-l14-50p", + "nema-l14-60p", + "nema-l15-20p", + "nema-l15-30p", + "nema-l15-50p", + "nema-l15-60p", + "nema-l21-20p", + "nema-l21-30p", + "nema-l22-20p", + "nema-l22-30p", + "cs6361c", + "cs6365c", + "cs8165c", + "cs8265c", + "cs8365c", + "cs8465c", + "ita-c", + "ita-e", + "ita-f", + "ita-ef", + "ita-g", + "ita-h", + "ita-i", + "ita-j", + "ita-k", + "ita-l", + "ita-m", + "ita-n", + "ita-o", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "usb-3-b", + "usb-3-micro-b", + "molex-micro-fit-1x2", + "molex-micro-fit-2x2", + "molex-micro-fit-2x3", + "molex-micro-fit-2x4", + "dc-terminal", + "saf-d-grid", + "neutrik-powercon-20", + "neutrik-powercon-32", + "neutrik-powercon-true1", + "neutrik-powercon-true1-top", + "ubiquiti-smartpower", + "hardwired", + "other", + "", + null + ], + "type": "string", + "description": "* `iec-60320-c6` - C6\n* `iec-60320-c8` - C8\n* `iec-60320-c14` - C14\n* `iec-60320-c16` - C16\n* `iec-60320-c18` - C18\n* `iec-60320-c20` - C20\n* `iec-60320-c22` - C22\n* `iec-60309-p-n-e-4h` - P+N+E 4H\n* `iec-60309-p-n-e-6h` - P+N+E 6H\n* `iec-60309-p-n-e-9h` - P+N+E 9H\n* `iec-60309-2p-e-4h` - 2P+E 4H\n* `iec-60309-2p-e-6h` - 2P+E 6H\n* `iec-60309-2p-e-9h` - 2P+E 9H\n* `iec-60309-3p-e-4h` - 3P+E 4H\n* `iec-60309-3p-e-6h` - 3P+E 6H\n* `iec-60309-3p-e-9h` - 3P+E 9H\n* `iec-60309-3p-n-e-4h` - 3P+N+E 4H\n* `iec-60309-3p-n-e-6h` - 3P+N+E 6H\n* `iec-60309-3p-n-e-9h` - 3P+N+E 9H\n* `iec-60906-1` - IEC 60906-1\n* `nbr-14136-10a` - 2P+T 10A (NBR 14136)\n* `nbr-14136-20a` - 2P+T 20A (NBR 14136)\n* `nema-1-15p` - NEMA 1-15P\n* `nema-5-15p` - NEMA 5-15P\n* `nema-5-20p` - NEMA 5-20P\n* `nema-5-30p` - NEMA 5-30P\n* `nema-5-50p` - NEMA 5-50P\n* `nema-6-15p` - NEMA 6-15P\n* `nema-6-20p` - NEMA 6-20P\n* `nema-6-30p` - NEMA 6-30P\n* `nema-6-50p` - NEMA 6-50P\n* `nema-10-30p` - NEMA 10-30P\n* `nema-10-50p` - NEMA 10-50P\n* `nema-14-20p` - NEMA 14-20P\n* `nema-14-30p` - NEMA 14-30P\n* `nema-14-50p` - NEMA 14-50P\n* `nema-14-60p` - NEMA 14-60P\n* `nema-15-15p` - NEMA 15-15P\n* `nema-15-20p` - NEMA 15-20P\n* `nema-15-30p` - NEMA 15-30P\n* `nema-15-50p` - NEMA 15-50P\n* `nema-15-60p` - NEMA 15-60P\n* `nema-l1-15p` - NEMA L1-15P\n* `nema-l5-15p` - NEMA L5-15P\n* `nema-l5-20p` - NEMA L5-20P\n* `nema-l5-30p` - NEMA L5-30P\n* `nema-l5-50p` - NEMA L5-50P\n* `nema-l6-15p` - NEMA L6-15P\n* `nema-l6-20p` - NEMA L6-20P\n* `nema-l6-30p` - NEMA L6-30P\n* `nema-l6-50p` - NEMA L6-50P\n* `nema-l10-30p` - NEMA L10-30P\n* `nema-l14-20p` - NEMA L14-20P\n* `nema-l14-30p` - NEMA L14-30P\n* `nema-l14-50p` - NEMA L14-50P\n* `nema-l14-60p` - NEMA L14-60P\n* `nema-l15-20p` - NEMA L15-20P\n* `nema-l15-30p` - NEMA L15-30P\n* `nema-l15-50p` - NEMA L15-50P\n* `nema-l15-60p` - NEMA L15-60P\n* `nema-l21-20p` - NEMA L21-20P\n* `nema-l21-30p` - NEMA L21-30P\n* `nema-l22-20p` - NEMA L22-20P\n* `nema-l22-30p` - NEMA L22-30P\n* `cs6361c` - CS6361C\n* `cs6365c` - CS6365C\n* `cs8165c` - CS8165C\n* `cs8265c` - CS8265C\n* `cs8365c` - CS8365C\n* `cs8465c` - CS8465C\n* `ita-c` - ITA Type C (CEE 7/16)\n* `ita-e` - ITA Type E (CEE 7/6)\n* `ita-f` - ITA Type F (CEE 7/4)\n* `ita-ef` - ITA Type E/F (CEE 7/7)\n* `ita-g` - ITA Type G (BS 1363)\n* `ita-h` - ITA Type H\n* `ita-i` - ITA Type I\n* `ita-j` - ITA Type J\n* `ita-k` - ITA Type K\n* `ita-l` - ITA Type L (CEI 23-50)\n* `ita-m` - ITA Type M (BS 546)\n* `ita-n` - ITA Type N\n* `ita-o` - ITA Type O\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `usb-3-b` - USB 3.0 Type B\n* `usb-3-micro-b` - USB 3.0 Micro B\n* `molex-micro-fit-1x2` - Molex Micro-Fit 1x2\n* `molex-micro-fit-2x2` - Molex Micro-Fit 2x2\n* `molex-micro-fit-2x3` - Molex Micro-Fit 2x3\n* `molex-micro-fit-2x4` - Molex Micro-Fit 2x4\n* `dc-terminal` - DC Terminal\n* `saf-d-grid` - Saf-D-Grid\n* `neutrik-powercon-20` - Neutrik powerCON (20A)\n* `neutrik-powercon-32` - Neutrik powerCON (32A)\n* `neutrik-powercon-true1` - Neutrik powerCON TRUE1\n* `neutrik-powercon-true1-top` - Neutrik powerCON TRUE1 TOP\n* `ubiquiti-smartpower` - Ubiquiti SmartPower\n* `hardwired` - Hardwired\n* `other` - Other", + "x-spec-enum-id": "aadcbe6ca854c1ed", + "nullable": true + }, + "maximum_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Maximum power draw (watts)" + }, + "allocated_draw": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true, + "description": "Allocated power draw (watts)" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkPrefixRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "prefix": { + "type": "string", + "minLength": 1 + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "container", + "active", + "reserved", + "deprecated" + ], + "type": "string", + "description": "* `container` - Container\n* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated", + "x-spec-enum-id": "026173ce39f2ee63" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "is_pool": { + "type": "boolean", + "title": "Is a pool", + "description": "All IP addresses within this prefix are considered usable" + }, + "mark_utilized": { + "type": "boolean", + "description": "Treat as fully utilized" + }, + "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": [ + "id" + ] + }, + "PatchedBulkProviderAccountRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "provider": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderRequest" + } + ] + }, + "name": { + "type": "string", + "default": "", + "maxLength": 100 + }, + "account": { + "type": "string", + "minLength": 1, + "title": "Account ID", + "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": [ + "id" + ] + }, + "PatchedBulkProviderNetworkRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "provider": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "service_id": { + "type": "string", + "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": [ + "id" + ] + }, + "PatchedBulkProviderRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Full name of the provider", + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "accounts": { + "type": "array", + "items": { + "type": "integer" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "asns": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkRIRRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "is_private": { + "type": "boolean", + "title": "Private", + "description": "IP space managed by this RIR is considered private" + }, + "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": [ + "id" + ] + }, + "PatchedBulkRackGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkRackRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "facility_id": { + "type": "string", + "nullable": true, + "maxLength": 50 + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ] + }, + "location": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefLocationRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "reserved", + "available", + "planned", + "active", + "deprecated" + ], + "type": "string", + "description": "* `reserved` - Reserved\n* `available` - Available\n* `planned` - Planned\n* `active` - Active\n* `deprecated` - Deprecated", + "x-spec-enum-id": "76eea4eef8804bcb" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "serial": { + "type": "string", + "title": "Serial number", + "maxLength": 50 + }, + "asset_tag": { + "type": "string", + "nullable": true, + "description": "A unique tag used to identify this rack", + "maxLength": 50 + }, + "rack_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRackTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "form_factor": { + "enum": [ + "2-post-frame", + "4-post-frame", + "4-post-cabinet", + "wall-frame", + "wall-frame-vertical", + "wall-cabinet", + "wall-cabinet-vertical", + "", + null + ], + "type": "string", + "description": "* `2-post-frame` - 2-post frame\n* `4-post-frame` - 4-post frame\n* `4-post-cabinet` - 4-post cabinet\n* `wall-frame` - Wall-mounted frame\n* `wall-frame-vertical` - Wall-mounted frame (vertical)\n* `wall-cabinet` - Wall-mounted cabinet\n* `wall-cabinet-vertical` - Wall-mounted cabinet (vertical)", + "x-spec-enum-id": "8a902fde21d48841", + "nullable": true + }, + "width": { + "enum": [ + 10, + 19, + 21, + 23 + ], + "type": "integer", + "description": "* `10` - 10 inches\n* `19` - 19 inches\n* `21` - 21 inches\n* `23` - 23 inches", + "x-spec-enum-id": "9b322795f297a9c3" + }, + "u_height": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "title": "Height (U)", + "description": "Height in rack units" + }, + "starting_unit": { + "type": "integer", + "maximum": 32767, + "minimum": 1, + "description": "Starting unit for rack" + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "max_weight": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "description": "Maximum load capacity for the rack" + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "desc_units": { + "type": "boolean", + "title": "Descending units", + "description": "Units are numbered top-to-bottom" + }, + "outer_width": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (width)" + }, + "outer_height": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (height)" + }, + "outer_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (depth)" + }, + "outer_unit": { + "enum": [ + "mm", + "in", + "", + null + ], + "type": "string", + "description": "* `mm` - Millimeters\n* `in` - Inches", + "x-spec-enum-id": "3d701848b66312c3", + "nullable": true + }, + "mounting_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails." + }, + "airflow": { + "enum": [ + "front-to-rear", + "rear-to-front", + "" + ], + "type": "string", + "description": "* `front-to-rear` - Front to rear\n* `rear-to-front` - Rear to front", + "x-spec-enum-id": "a784734d07ef1b3c" + }, + "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": [ + "id" + ] + }, + "PatchedBulkRackReservationRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "rack": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefRackRequest" + } + ] + }, + "units": { + "type": "array", + "items": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + } + }, + "status": { + "enum": [ + "pending", + "active", + "stale" + ], + "type": "string", + "description": "* `pending` - Pending\n* `active` - Active\n* `stale` - Stale", + "x-spec-enum-id": "ed6038a4deee151c" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "minLength": 1, + "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": [ + "id" + ] + }, + "PatchedBulkRackRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id" + ] + }, + "PatchedBulkRackTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "manufacturer": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefManufacturerRequest" + } + ] + }, + "model": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "form_factor": { + "enum": [ + "2-post-frame", + "4-post-frame", + "4-post-cabinet", + "wall-frame", + "wall-frame-vertical", + "wall-cabinet", + "wall-cabinet-vertical", + "", + null + ], + "type": "string", + "description": "* `2-post-frame` - 2-post frame\n* `4-post-frame` - 4-post frame\n* `4-post-cabinet` - 4-post cabinet\n* `wall-frame` - Wall-mounted frame\n* `wall-frame-vertical` - Wall-mounted frame (vertical)\n* `wall-cabinet` - Wall-mounted cabinet\n* `wall-cabinet-vertical` - Wall-mounted cabinet (vertical)", + "x-spec-enum-id": "8a902fde21d48841", + "nullable": true + }, + "width": { + "enum": [ + 10, + 19, + 21, + 23 + ], + "type": "integer", + "description": "* `10` - 10 inches\n* `19` - 19 inches\n* `21` - 21 inches\n* `23` - 23 inches", + "x-spec-enum-id": "9b322795f297a9c3" + }, + "u_height": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "title": "Height (U)", + "description": "Height in rack units" + }, + "starting_unit": { + "type": "integer", + "maximum": 32767, + "minimum": 1, + "description": "Starting unit for rack" + }, + "desc_units": { + "type": "boolean", + "title": "Descending units", + "description": "Units are numbered top-to-bottom" + }, + "outer_width": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (width)" + }, + "outer_height": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (height)" + }, + "outer_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Outer dimension of rack (depth)" + }, + "outer_unit": { + "enum": [ + "mm", + "in", + "", + null + ], + "type": "string", + "description": "* `mm` - Millimeters\n* `in` - Inches", + "x-spec-enum-id": "3d701848b66312c3", + "nullable": true + }, + "weight": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "max_weight": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "description": "Maximum load capacity for the rack" + }, + "weight_unit": { + "enum": [ + "kg", + "g", + "lb", + "oz", + "", + null + ], + "type": "string", + "description": "* `kg` - Kilograms\n* `g` - Grams\n* `lb` - Pounds\n* `oz` - Ounces", + "x-spec-enum-id": "2235ce3f404afbc0", + "nullable": true + }, + "mounting_depth": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "Maximum depth of a mounted device, in millimeters. For four-post racks, this is the distance between the front and rear rails." + }, + "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": [ + "id" + ] + }, + "PatchedBulkRearPortRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "module": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "front_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RearPortMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mark_connected": { + "type": "boolean", + "description": "Treat as if a cable is connected" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkRearPortTemplateRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "device_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "module_type": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefModuleTypeRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "name": { + "type": "string", + "minLength": 1, + "description": "{module} is accepted as a substitution for the module bay position when attached to a module type.", + "maxLength": 64 + }, + "label": { + "type": "string", + "description": "Physical label", + "maxLength": 64 + }, + "type": { + "enum": [ + "8p8c", + "8p6c", + "8p4c", + "8p2c", + "6p6c", + "6p4c", + "6p2c", + "4p4c", + "4p2c", + "gg45", + "tera-4p", + "tera-2p", + "tera-1p", + "110-punch", + "bnc", + "f", + "n", + "mrj21", + "fc", + "fc-pc", + "fc-upc", + "fc-apc", + "lc", + "lc-pc", + "lc-upc", + "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", + "lsh", + "lsh-pc", + "lsh-upc", + "lsh-apc", + "lx5", + "lx5-pc", + "lx5-upc", + "lx5-apc", + "mpo", + "mtrj", + "sc", + "sc-pc", + "sc-upc", + "sc-apc", + "st", + "cs", + "sn", + "sma-905", + "sma-906", + "urm-p2", + "urm-p4", + "urm-p8", + "splice", + "usb-a", + "usb-b", + "usb-c", + "usb-mini-a", + "usb-mini-b", + "usb-micro-a", + "usb-micro-b", + "usb-micro-ab", + "other" + ], + "type": "string", + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "positions": { + "type": "integer", + "maximum": 1024, + "minimum": 1 + }, + "front_ports": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RearPortTemplateMappingRequest" + } + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkRegionRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedRegionRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkRoleRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "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": [ + "id" + ] + }, + "PatchedBulkRouteTargetRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Route target value (formatted in accordance with RFC 4360)", + "maxLength": 21 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkSavedFilterRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "user": { + "type": "integer", + "nullable": true + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "enabled": { + "type": "boolean" + }, + "shared": { + "type": "boolean" + }, + "parameters": {}, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkServiceRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "parent_object_type": { + "type": "string" + }, + "parent_object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "protocol": { + "enum": [ + "tcp", + "udp", + "sctp" + ], + "type": "string", + "description": "* `tcp` - TCP\n* `udp` - UDP\n* `sctp` - SCTP", + "x-spec-enum-id": "e4b15bec749a2a32" + }, + "ports": { + "type": "array", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 1 + }, + "title": "Port numbers" + }, + "ipaddresses": { + "type": "array", + "items": { + "type": "integer" + } + }, + "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": [ + "id" + ] + }, + "PatchedBulkServiceTemplateRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "protocol": { + "enum": [ + "tcp", + "udp", + "sctp" + ], + "type": "string", + "description": "* `tcp` - TCP\n* `udp` - UDP\n* `sctp` - SCTP", + "x-spec-enum-id": "e4b15bec749a2a32" + }, + "ports": { + "type": "array", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 1 + }, + "title": "Port numbers" + }, + "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": [ + "id" + ] + }, + "PatchedBulkSiteGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedSiteGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkSiteRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Full name of the site", + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "status": { + "enum": [ + "planned", + "staging", + "active", + "decommissioning", + "retired" + ], + "type": "string", + "description": "* `planned` - Planned\n* `staging` - Staging\n* `active` - Active\n* `decommissioning` - Decommissioning\n* `retired` - Retired", + "x-spec-enum-id": "1cf60831fbb35e7f" + }, + "region": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRegionRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefSiteGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "facility": { + "type": "string", + "description": "Local facility ID or description", + "maxLength": 50 + }, + "time_zone": { + "type": "string", + "nullable": true, + "minLength": 1 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "physical_address": { + "type": "string", + "description": "Physical location of the building", + "maxLength": 200 + }, + "shipping_address": { + "type": "string", + "description": "If different from the physical address", + "maxLength": 200 + }, + "latitude": { + "type": "number", + "format": "double", + "maximum": 90.0, + "minimum": -90.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "longitude": { + "type": "number", + "format": "double", + "maximum": 180.0, + "minimum": -180.0, + "nullable": true, + "description": "GPS coordinate in decimal format (xx.yyyyyy)" + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "asns": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkSubscriptionRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "object_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64" + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkTableConfigRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "object_type": { + "type": "string" + }, + "table": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "user": { + "type": "integer", + "nullable": true + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "enabled": { + "type": "boolean" + }, + "shared": { + "type": "boolean" + }, + "columns": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + }, + "ordering": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "nullable": true + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkTagRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "pattern": "^[-\\w]+$", + "maxLength": 100 + }, + "color": { + "type": "string", + "minLength": 1, + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "weight": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "object_types": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkTenantGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedTenantGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkTenantRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkTokenRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "version": { + "enum": [ + 1, + 2 + ], + "type": "integer", + "description": "* `1` - v1\n* `2` - v2", + "x-spec-enum-id": "b5df70f0bffd12cb", + "minimum": 0, + "maximum": 32767 + }, + "user": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefUserRequest" + } + ] + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "expires": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_used": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "enabled": { + "type": "boolean", + "description": "Disable to temporarily revoke this token without deleting it." + }, + "write_enabled": { + "type": "boolean", + "description": "Permit create/update/delete operations using this token" + }, + "pepper_id": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true, + "description": "ID of the cryptographic pepper used to hash the token (v2 only)" + }, + "token": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkTunnelGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkTunnelRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "status": { + "enum": [ + "planned", + "active", + "disabled" + ], + "type": "string", + "description": "* `planned` - Planned\n* `active` - Active\n* `disabled` - Disabled", + "x-spec-enum-id": "2431ef62c418f485" + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTunnelGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "encapsulation": { + "enum": [ + "ipsec-transport", + "ipsec-tunnel", + "ip-ip", + "gre", + "wireguard", + "openvpn", + "l2tp", + "pptp" + ], + "type": "string", + "description": "* `ipsec-transport` - IPsec - Transport\n* `ipsec-tunnel` - IPsec - Tunnel\n* `ip-ip` - IP-in-IP\n* `gre` - GRE\n* `wireguard` - WireGuard\n* `openvpn` - OpenVPN\n* `l2tp` - L2TP\n* `pptp` - PPTP", + "x-spec-enum-id": "4f3254459f0e94f0" + }, + "ipsec_profile": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPSecProfileRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tunnel_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkTunnelTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "tunnel": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefTunnelRequest" + } + ] + }, + "role": { + "enum": [ + "peer", + "hub", + "spoke" + ], + "type": "string", + "description": "* `peer` - Peer\n* `hub` - Hub\n* `spoke` - Spoke", + "x-spec-enum-id": "0b3bfadcebd86b58" + }, + "termination_type": { + "type": "string" + }, + "termination_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 0, + "format": "int64", + "nullable": true + }, + "outside_ip": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkUserRequest": { + "type": "object", + "description": "Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during\nvalidation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)", + "properties": { + "id": { + "type": "integer" + }, + "username": { + "type": "string", + "minLength": 1, + "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + "pattern": "^[\\w.@+-]+$", + "maxLength": 150 + }, + "password": { + "type": "string", + "writeOnly": true, + "minLength": 1, + "maxLength": 128 + }, + "first_name": { + "type": "string", + "maxLength": 150 + }, + "last_name": { + "type": "string", + "maxLength": 150 + }, + "email": { + "type": "string", + "format": "email", + "title": "Email address", + "maxLength": 254 + }, + "is_active": { + "type": "boolean", + "title": "Active", + "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." + }, + "date_joined": { + "type": "string", + "format": "date-time" + }, + "last_login": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "groups": { + "type": "array", + "items": { + "type": "integer" + } + }, + "permissions": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVLANGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "vid_ranges": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegerRangeRequest" + } + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkVLANRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vid": { + "type": "integer", + "maximum": 4094, + "minimum": 1, + "title": "VLAN ID", + "description": "Numeric VLAN ID (1-4094)" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "deprecated" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `deprecated` - Deprecated", + "x-spec-enum-id": "ca933c38b935e547" + }, + "role": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefRoleRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "qinq_role": { + "enum": [ + "svlan", + "cvlan", + null + ], + "type": "string", + "description": "* `svlan` - Service\n* `cvlan` - Customer", + "x-spec-enum-id": "fa0abd59fb1a7312", + "nullable": true + }, + "qinq_svlan": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedVLANRequest" + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkVLANTranslationPolicyRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "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" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVLANTranslationRuleRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "policy": { + "type": "integer" + }, + "local_vid": { + "type": "integer", + "maximum": 4094, + "minimum": 1, + "title": "Local VLAN ID", + "description": "Numeric VLAN ID (1-4094)" + }, + "remote_vid": { + "type": "integer", + "maximum": 4094, + "minimum": 1, + "title": "Remote VLAN ID", + "description": "Numeric VLAN ID (1-4094)" + }, + "description": { + "type": "string", + "maxLength": 200 + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVMInterfaceRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "virtual_machine": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualMachineRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "enabled": { + "type": "boolean" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedVMInterfaceRequest" + } + ], + "nullable": true + }, + "bridge": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedVMInterfaceRequest" + } + ], + "nullable": true + }, + "mtu": { + "type": "integer", + "maximum": 65536, + "minimum": 1, + "nullable": true + }, + "primary_mac_address": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefMACAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "mode": { + "enum": [ + "access", + "tagged", + "tagged-all", + "q-in-q", + "" + ], + "type": "string", + "description": "* `access` - Access\n* `tagged` - Tagged\n* `tagged-all` - Tagged (All)\n* `q-in-q` - Q-in-Q (802.1ad)", + "x-spec-enum-id": "84129b71b974ebe5" + }, + "untagged_vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tagged_vlans": { + "type": "array", + "items": { + "type": "integer" + } + }, + "qinq_svlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vlan_translation_policy": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANTranslationPolicyRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vrf": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVRFRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVRFRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "rd": { + "type": "string", + "nullable": true, + "title": "Route distinguisher", + "description": "Unique route distinguisher (as defined in RFC 4364)", + "maxLength": 21 + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "enforce_unique": { + "type": "boolean", + "title": "Enforce unique space", + "description": "Prevent duplicate prefixes/IP addresses within this VRF" + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + }, + "import_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "export_targets": { + "type": "array", + "items": { + "type": "integer" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVirtualChassisRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "domain": { + "type": "string", + "maxLength": 30 + }, + "master": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedDeviceRequest" + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkVirtualCircuitRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "cid": { + "type": "string", + "minLength": 1, + "title": "Circuit ID", + "description": "Unique circuit ID", + "maxLength": 100 + }, + "provider_network": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefProviderNetworkRequest" + } + ] + }, + "provider_account": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefProviderAccountRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "type": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualCircuitTypeRequest" + } + ] + }, + "status": { + "enum": [ + "planned", + "provisioning", + "active", + "offline", + "deprovisioning", + "decommissioned" + ], + "type": "string", + "description": "* `planned` - Planned\n* `provisioning` - Provisioning\n* `active` - Active\n* `offline` - Offline\n* `deprovisioning` - Deprovisioning\n* `decommissioned` - Decommissioned", + "x-spec-enum-id": "0a239d878b6666a4" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + "id" + ] + }, + "PatchedBulkVirtualCircuitTerminationRequest": { + "type": "object", + "description": "Adds support for custom fields and tags.", + "properties": { + "id": { + "type": "integer" + }, + "virtual_circuit": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualCircuitRequest" + } + ] + }, + "role": { + "enum": [ + "peer", + "hub", + "spoke" + ], + "type": "string", + "description": "* `peer` - Peer\n* `hub` - Hub\n* `spoke` - Spoke", + "x-spec-enum-id": "0b3bfadcebd86b58" + }, + "interface": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefInterfaceRequest" + } + ] + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVirtualCircuitTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from OrganizationalModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "color": { + "type": "string", + "pattern": "^[0-9a-f]{6}$", + "maxLength": 6 + }, + "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": [ + "id" + ] + }, + "PatchedBulkVirtualDeviceContextRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ] + }, + "identifier": { + "type": "integer", + "maximum": 32767, + "minimum": 0, + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip4": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip6": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "planned", + "offline" + ], + "type": "string", + "description": "* `active` - Active\n* `planned` - Planned\n* `offline` - Offline", + "x-spec-enum-id": "0e2c0919d51b83cb" + }, + "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": [ + "id" + ] + }, + "PatchedBulkVirtualDiskRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "virtual_machine": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefVirtualMachineRequest" + } + ] + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "size": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkVirtualMachineTypeRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "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": [ + "id" + ] + }, + "PatchedBulkVirtualMachineWithConfigContextRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "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", + "active", + "planned", + "staged", + "failed", + "decommissioning", + "paused" + ], + "type": "string", + "description": "* `offline` - Offline\n* `active` - Active\n* `planned` - Planned\n* `staged` - Staged\n* `failed` - Failed\n* `decommissioning` - Decommissioning\n* `paused` - Paused", + "x-spec-enum-id": "effecc3b94e0b74b" + }, + "start_on_boot": { + "enum": [ + "on", + "off", + "laststate" + ], + "type": "string", + "description": "* `on` - On\n* `off` - Off\n* `laststate` - Last State", + "x-spec-enum-id": "610e33fc2fde73d6" + }, + "site": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefSiteRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "cluster": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefClusterRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "device": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefDeviceRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "platform": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefPlatformRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip4": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "primary_ip6": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefIPAddressRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "vcpus": { + "type": "number", + "format": "double", + "maximum": 10000, + "minimum": 0.01, + "exclusiveMaximum": true, + "nullable": true + }, + "memory": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "disk": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "description": { + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "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": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefConfigTemplateRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkWebhookRequest": { + "type": "object", + "description": "Adds an `owner` field for models which have a ForeignKey to users.Owner.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 150 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "payload_url": { + "type": "string", + "minLength": 1, + "title": "URL", + "description": "This URL will be called using the HTTP method defined when the webhook is called. Jinja2 template processing is supported with the same context as the request body.", + "maxLength": 500 + }, + "http_method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "type": "string", + "description": "* `GET` - GET\n* `POST` - POST\n* `PUT` - PUT\n* `PATCH` - PATCH\n* `DELETE` - DELETE", + "x-spec-enum-id": "867bf764d3b1eeaa" + }, + "http_content_type": { + "type": "string", + "minLength": 1, + "description": "The complete list of official content types is available here.", + "maxLength": 100 + }, + "additional_headers": { + "type": "string", + "description": "User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. Headers should be defined in the format Name: Value. Jinja2 template processing is supported with the same context as the request body (below)." + }, + "body_template": { + "type": "string", + "description": "Jinja2 template for a custom request body. If blank, a JSON object representing the change will be included. Available context data includes: event, model, timestamp, username, request_id, and data." + }, + "secret": { + "type": "string", + "description": "When provided, the request will include a X-Hook-Signature header containing a HMAC hex digest of the payload body using the secret as the key. The secret is not transmitted in the request.", + "maxLength": 255 + }, + "ssl_verification": { + "type": "boolean", + "description": "Enable SSL certificate verification. Disable with caution!" + }, + "ca_file_path": { + "type": "string", + "nullable": true, + "description": "The specific CA certificate file to use for SSL verification. Leave blank to use the system defaults.", + "maxLength": 4096 + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkWirelessLANGroupRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from NestedGroupModel.", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/NestedWirelessLANGroupRequest" + } + ], + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NestedTagRequest" + } + }, + "custom_fields": { + "type": "object", + "additionalProperties": {} + }, + "owner": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefOwnerRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "comments": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "PatchedBulkWirelessLANRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "ssid": { + "type": "string", + "minLength": 1, + "maxLength": 32 + }, + "description": { + "type": "string", + "maxLength": 200 + }, + "group": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefWirelessLANGroupRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "status": { + "enum": [ + "active", + "reserved", + "disabled", + "deprecated", + "" + ], + "type": "string", + "description": "* `active` - Active\n* `reserved` - Reserved\n* `disabled` - Disabled\n* `deprecated` - Deprecated", + "x-spec-enum-id": "e5549d7370ce2e6c" + }, + "vlan": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefVLANRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "scope_type": { + "type": "string", + "nullable": true + }, + "scope_id": { + "type": "integer", + "nullable": true + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "auth_type": { + "enum": [ + "open", + "wep", + "wpa-personal", + "wpa-enterprise", + "" + ], + "type": "string", + "description": "* `open` - Open\n* `wep` - WEP\n* `wpa-personal` - WPA Personal (PSK)\n* `wpa-enterprise` - WPA Enterprise", + "x-spec-enum-id": "e917c12aac765910" + }, + "auth_cipher": { + "enum": [ + "auto", + "tkip", + "aes", + "" + ], + "type": "string", + "description": "* `auto` - Auto\n* `tkip` - TKIP\n* `aes` - AES", + "x-spec-enum-id": "42f867e89988bb0c" + }, + "auth_psk": { + "type": "string", + "title": "Pre-shared key", + "maxLength": 64 + }, + "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": [ + "id" + ] + }, + "PatchedBulkWirelessLinkRequest": { + "type": "object", + "description": "Base serializer class for models inheriting from PrimaryModel.", + "properties": { + "id": { + "type": "integer" + }, + "interface_a": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefInterfaceRequest" + } + ] + }, + "interface_b": { + "oneOf": [ + { + "type": "integer" + }, + { + "$ref": "#/components/schemas/BriefInterfaceRequest" + } + ] + }, + "ssid": { + "type": "string", + "maxLength": 32 + }, + "status": { + "enum": [ + "connected", + "planned", + "decommissioning" + ], + "type": "string", + "description": "* `connected` - Connected\n* `planned` - Planned\n* `decommissioning` - Decommissioning", + "x-spec-enum-id": "80d251a40f3a3144" + }, + "tenant": { + "oneOf": [ + { + "type": "integer" + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/BriefTenantRequest" + } + ], + "nullable": true + } + ], + "nullable": true + }, + "auth_type": { + "enum": [ + "open", + "wep", + "wpa-personal", + "wpa-enterprise", + "" + ], + "type": "string", + "description": "* `open` - Open\n* `wep` - WEP\n* `wpa-personal` - WPA Personal (PSK)\n* `wpa-enterprise` - WPA Enterprise", + "x-spec-enum-id": "e917c12aac765910" + }, + "auth_cipher": { + "enum": [ + "auto", + "tkip", + "aes", + "" + ], + "type": "string", + "description": "* `auto` - Auto\n* `tkip` - TKIP\n* `aes` - AES", + "x-spec-enum-id": "42f867e89988bb0c" + }, + "auth_psk": { + "type": "string", + "title": "Pre-shared key", + "maxLength": 64 + }, + "distance": { + "type": "number", + "format": "double", + "maximum": 1000000, + "minimum": -1000000, + "exclusiveMaximum": true, + "exclusiveMinimum": true, + "nullable": true + }, + "distance_unit": { + "enum": [ + "km", + "m", + "mi", + "ft", + "", + null + ], + "type": "string", + "description": "* `km` - Kilometers\n* `m` - Meters\n* `mi` - Miles\n* `ft` - Feet", + "x-spec-enum-id": "b1169a409430c02e", + "nullable": true + }, + "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": [ + "id" + ] + }, "PatchedCableBundleRequest": { "type": "object", "description": "Base serializer class for models inheriting from PrimaryModel.", @@ -264808,6 +298057,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -264842,8 +298095,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -264972,6 +298225,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -265006,8 +298263,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -269221,6 +302478,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -269255,8 +302516,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -269385,6 +302646,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -269419,8 +302684,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -277132,6 +310397,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -277166,8 +310435,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "label": { "type": "string", @@ -277198,6 +310467,10 @@ "LC/PC", "LC/UPC", "LC/APC", + "MU", + "MU/PC", + "MU/UPC", + "MU/APC", "LSH", "LSH/PC", "LSH/UPC", @@ -277453,6 +310726,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -277487,8 +310764,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -277621,6 +310898,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -277655,8 +310936,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "label": { "type": "string", @@ -277687,6 +310968,10 @@ "LC/PC", "LC/UPC", "LC/APC", + "MU", + "MU/PC", + "MU/UPC", + "MU/APC", "LSH", "LSH/PC", "LSH/UPC", @@ -277883,6 +311168,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -277917,8 +311206,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -287946,6 +321235,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -287980,8 +321273,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -288115,6 +321408,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -288149,8 +321446,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -292474,6 +325771,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -292508,8 +325809,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", @@ -292643,6 +325944,10 @@ "lc-pc", "lc-upc", "lc-apc", + "mu", + "mu-pc", + "mu-upc", + "mu-apc", "lsh", "lsh-pc", "lsh-upc", @@ -292677,8 +325982,8 @@ "other" ], "type": "string", - "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", - "x-spec-enum-id": "2696b7065f33307c" + "description": "* `8p8c` - 8P8C\n* `8p6c` - 8P6C\n* `8p4c` - 8P4C\n* `8p2c` - 8P2C\n* `6p6c` - 6P6C\n* `6p4c` - 6P4C\n* `6p2c` - 6P2C\n* `4p4c` - 4P4C\n* `4p2c` - 4P2C\n* `gg45` - GG45\n* `tera-4p` - TERA 4P\n* `tera-2p` - TERA 2P\n* `tera-1p` - TERA 1P\n* `110-punch` - 110 Punch\n* `bnc` - BNC\n* `f` - F Connector\n* `n` - N Connector\n* `mrj21` - MRJ21\n* `fc` - FC\n* `fc-pc` - FC/PC\n* `fc-upc` - FC/UPC\n* `fc-apc` - FC/APC\n* `lc` - LC\n* `lc-pc` - LC/PC\n* `lc-upc` - LC/UPC\n* `lc-apc` - LC/APC\n* `mu` - MU\n* `mu-pc` - MU/PC\n* `mu-upc` - MU/UPC\n* `mu-apc` - MU/APC\n* `lsh` - LSH\n* `lsh-pc` - LSH/PC\n* `lsh-upc` - LSH/UPC\n* `lsh-apc` - LSH/APC\n* `lx5` - LX.5\n* `lx5-pc` - LX.5/PC\n* `lx5-upc` - LX.5/UPC\n* `lx5-apc` - LX.5/APC\n* `mpo` - MPO\n* `mtrj` - MTRJ\n* `sc` - SC\n* `sc-pc` - SC/PC\n* `sc-upc` - SC/UPC\n* `sc-apc` - SC/APC\n* `st` - ST\n* `cs` - CS\n* `sn` - SN\n* `sma-905` - SMA 905\n* `sma-906` - SMA 906\n* `urm-p2` - URM-P2\n* `urm-p4` - URM-P4\n* `urm-p8` - URM-P8\n* `splice` - Splice\n* `usb-a` - USB Type A\n* `usb-b` - USB Type B\n* `usb-c` - USB Type C\n* `usb-mini-a` - USB Mini A\n* `usb-mini-b` - USB Mini B\n* `usb-micro-a` - USB Micro A\n* `usb-micro-b` - USB Micro B\n* `usb-micro-ab` - USB Micro AB\n* `other` - Other", + "x-spec-enum-id": "b64d6804afec405c" }, "color": { "type": "string", diff --git a/docs/installation/2-redis.md b/docs/installation/2-redis.md index c29deb5c2..f412cced1 100644 --- a/docs/installation/2-redis.md +++ b/docs/installation/2-redis.md @@ -8,7 +8,10 @@ sudo apt install -y redis-server ``` -Before continuing, verify that your installed version of Redis is at least v4.0: +Before continuing, verify that your installed version of Redis is at least v6.0: + +!!! warning "Redis v5.x is deprecated" + Support for Redis versions older than 6.0 is deprecated and will be removed in NetBox v4.7. ```no-highlight redis-server -v diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 9aefd6c55..acb15725b 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -40,10 +40,10 @@ NetBox requires the following dependencies: | NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation | |:--------------:|:----------:|:----------:|:--------------:|:---------:|:-----------------------------------------------------------------------------------------:| -| 4.6 | 3.12 | 3.14 | 14 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.6.0/docs/installation/index.md) | -| 4.5 | 3.12 | 3.14 | 14 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.5.0/docs/installation/index.md) | -| 4.4 | 3.10 | 3.12 | 14 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.4.0/docs/installation/index.md) | -| 4.3 | 3.10 | 3.12 | 14 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.3.0/docs/installation/index.md) | +| 4.6 | 3.12 | 3.14 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.6.0/docs/installation/index.md) | +| 4.5 | 3.12 | 3.14 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.5.0/docs/installation/index.md) | +| 4.4 | 3.10 | 3.12 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.4.0/docs/installation/index.md) | +| 4.3 | 3.10 | 3.12 | 14 | 5.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.3.0/docs/installation/index.md) | | 4.2 | 3.10 | 3.12 | 13 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.2.0/docs/installation/index.md) | | 4.1 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.1.0/docs/installation/index.md) | | 4.0 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.0.0/docs/installation/index.md) | diff --git a/docs/models/ipam/vlan.md b/docs/models/ipam/vlan.md index 58fc9f551..545ea6553 100644 --- a/docs/models/ipam/vlan.md +++ b/docs/models/ipam/vlan.md @@ -2,6 +2,12 @@ A Virtual LAN (VLAN) represents an isolated layer two domain, identified by a name and a numeric ID (1-4094) as defined in [IEEE 802.1Q](https://en.wikipedia.org/wiki/IEEE_802.1Q). VLANs are arranged into [VLAN groups](./vlangroup.md) to define scope and to enforce uniqueness. +## Bulk Creation + +Multiple VLANs can be created at once by selecting the "Bulk Create" tab on the VLAN creation form. Enter the desired VLAN IDs and/or ID ranges as a comma-separated list (e.g. `100,200-210,4000-4010`). The string `{vid}` may be embedded in the name field as a placeholder for each VLAN's ID; for example, `VLAN-{vid}` yields `VLAN-100`, `VLAN-200`, and so on. All other attributes (status, role, tenant, etc.) are applied to every new VLAN. + +The operation is atomic: if any VLAN fails validation (for example, a VLAN ID falling outside the assigned group's permitted ranges), no VLANs are created. + ## Fields ### ID diff --git a/docs/plugins/development/ui-components.md b/docs/plugins/development/ui-components.md index 45766b201..f148443e3 100644 --- a/docs/plugins/development/ui-components.md +++ b/docs/plugins/development/ui-components.md @@ -135,29 +135,32 @@ panels.ObjectsTablePanel( The following classes are available to represent object attributes within an ObjectAttributesPanel. Additionally, plugins can subclass `netbox.ui.attrs.ObjectAttribute` to create custom classes. -| Class | Description | -|------------------------------------------|--------------------------------------------------| -| `netbox.ui.attrs.AddressAttr` | A physical or mailing address. | -| `netbox.ui.attrs.BooleanAttr` | A boolean value | -| `netbox.ui.attrs.ChoiceAttr` | A selection from a set of choices | -| `netbox.ui.attrs.ColorAttr` | A color expressed in RGB | -| `netbox.ui.attrs.DateTimeAttr` | A date or datetime value | -| `netbox.ui.attrs.GenericForeignKeyAttr` | A related object via a generic foreign key | -| `netbox.ui.attrs.GPSCoordinatesAttr` | GPS coordinates (latitude and longitude) | -| `netbox.ui.attrs.ImageAttr` | An attached image (displays the image) | -| `netbox.ui.attrs.NestedObjectAttr` | A related nested object (includes ancestors) | -| `netbox.ui.attrs.NumericAttr` | An integer or float value | -| `netbox.ui.attrs.RelatedObjectAttr` | A related object | -| `netbox.ui.attrs.RelatedObjectListAttr` | A list of related objects | -| `netbox.ui.attrs.TemplatedAttr` | Renders an attribute using a custom template | -| `netbox.ui.attrs.TextAttr` | A string (text) value | -| `netbox.ui.attrs.TimezoneAttr` | A timezone with annotated offset | -| `netbox.ui.attrs.UtilizationAttr` | A numeric value expressed as a utilization graph | +| Class | Description | +|-----------------------------------------|-----------------------------------------------------| +| `netbox.ui.attrs.AddressAttr` | A physical or mailing address. | +| `netbox.ui.attrs.ArrayAttr` | An array of values, shown as a comma-separated list | +| `netbox.ui.attrs.BooleanAttr` | A boolean value | +| `netbox.ui.attrs.ChoiceAttr` | A selection from a set of choices | +| `netbox.ui.attrs.ColorAttr` | A color expressed in RGB | +| `netbox.ui.attrs.DateTimeAttr` | A date or datetime value | +| `netbox.ui.attrs.GenericForeignKeyAttr` | A related object via a generic foreign key | +| `netbox.ui.attrs.GPSCoordinatesAttr` | GPS coordinates (latitude and longitude) | +| `netbox.ui.attrs.ImageAttr` | An attached image (displays the image) | +| `netbox.ui.attrs.NestedObjectAttr` | A related nested object (includes ancestors) | +| `netbox.ui.attrs.NumericAttr` | An integer or float value | +| `netbox.ui.attrs.RelatedObjectAttr` | A related object | +| `netbox.ui.attrs.RelatedObjectListAttr` | A list of related objects | +| `netbox.ui.attrs.TemplatedAttr` | Renders an attribute using a custom template | +| `netbox.ui.attrs.TextAttr` | A string (text) value | +| `netbox.ui.attrs.TimezoneAttr` | A timezone with annotated offset | +| `netbox.ui.attrs.UtilizationAttr` | A numeric value expressed as a utilization graph | ::: netbox.ui.attrs.ObjectAttribute ::: netbox.ui.attrs.AddressAttr +::: netbox.ui.attrs.ArrayAttr + ::: netbox.ui.attrs.BooleanAttr ::: netbox.ui.attrs.ChoiceAttr diff --git a/docs/release-notes/version-4.6.md b/docs/release-notes/version-4.6.md index f4ea93052..0e152e5cb 100644 --- a/docs/release-notes/version-4.6.md +++ b/docs/release-notes/version-4.6.md @@ -1,5 +1,51 @@ # NetBox v4.6 +## v4.6.3 (2026-06-16) + +### Enhancements + +* [#17598](https://github.com/netbox-community/netbox/issues/17598) - Add bulk creation support for VLANs +* [#21666](https://github.com/netbox-community/netbox/issues/21666) - Add MU connector type for fiber ports and cables +* [#22361](https://github.com/netbox-community/netbox/issues/22361) - Introduce an `ArrayAttr` UI panel attribute for rendering array field values +* [#22457](https://github.com/netbox-community/netbox/issues/22457) - Use `hmac.compare_digest()` for constant-time authentication of API tokens + +### Performance Improvements + +* [#21870](https://github.com/netbox-community/netbox/issues/21870) - Optimize prefix availability calculations +* [#22375](https://github.com/netbox-community/netbox/issues/22375) - Improve efficiency of filtering VLANs by interface + +### Bug Fixes + +* [#21338](https://github.com/netbox-community/netbox/issues/21338) - Include connected endpoint data in interface webhooks generated during cable creation +* [#21895](https://github.com/netbox-community/netbox/issues/21895) - Restore pagination controls for job log entries (previously limited to 50 rows) +* [#22210](https://github.com/netbox-community/netbox/issues/22210) - Respect saved filters when rendering IPAM child availability views in additional tabs +* [#22237](https://github.com/netbox-community/netbox/issues/22237) - Fix server error when opening the standalone "Add Table Configuration" page +* [#22245](https://github.com/netbox-community/netbox/issues/22245) - Include the `id` field in the OpenAPI request schemas for bulk PATCH/PUT endpoints +* [#22251](https://github.com/netbox-community/netbox/issues/22251) - Re-parent child module bays when a multi-bay module is moved to a new bay +* [#22273](https://github.com/netbox-community/netbox/issues/22273) - Fix migration failure when a service has several thousand ports defined +* [#22303](https://github.com/netbox-community/netbox/issues/22303) - Add the missing `fields` parameter to the OpenAPI schema +* [#22324](https://github.com/netbox-community/netbox/issues/22324) - Fix GraphQL filtering of custom field choice set extra choices +* [#22340](https://github.com/netbox-community/netbox/issues/22340) - Display a token's allowed IPs as comma-separated strings rather than `IPNetwork` objects +* [#22346](https://github.com/netbox-community/netbox/issues/22346) - Render SSO/SAML authentication failures as a login page message instead of an HTTP 500 error +* [#22357](https://github.com/netbox-community/netbox/issues/22357) - Remove the unused `local_context_data` field from `dcim.Module` (which no longer inherits from `ConfigContextModel`) +* [#22376](https://github.com/netbox-community/netbox/issues/22376) - Fix `AssertionError` in event rule script jobs when a device type has an image attached +* [#22388](https://github.com/netbox-community/netbox/issues/22388) - Pin redis-py to <8.0 to avoid a startup failure on older Redis releases +* [#22397](https://github.com/netbox-community/netbox/issues/22397) - Fix `AttributeError` exception when an unauthenticated user attempts to export devices +* [#22399](https://github.com/netbox-community/netbox/issues/22399) - Enforce object permissions on the related object when serving static media +* [#22427](https://github.com/netbox-community/netbox/issues/22427) - Validate `JSONFilter.path` to prevent ORM operator injection over JSONField contents in the GraphQL API +* [#22429](https://github.com/netbox-community/netbox/issues/22429) - Enforce `ObjectPermission` constraints on `grant_token` in the REST API +* [#22431](https://github.com/netbox-community/netbox/issues/22431) - Use a cryptographically secure random number generator when generating API tokens +* [#22444](https://github.com/netbox-community/netbox/issues/22444) - Fix `KeyError` exception on the power feed detail view when the locale is not English +* [#22448](https://github.com/netbox-community/netbox/issues/22448) - Ensure all object representations are escaped under `handle_protectederror()` +* [#22454](https://github.com/netbox-community/netbox/issues/22454) - Fix serialization of decimal custom field values to avoid spurious changelog entries +* [#22466](https://github.com/netbox-community/netbox/issues/22466) - Fix test failure against SSL-enabled PosgtreSQL + +### Deprecations + +* [#22392](https://github.com/netbox-community/netbox/issues/22392) - Deprecate support for Redis 5.x (to be removed in v4.7) + +--- + ## v4.6.2 (2026-06-02) ### Enhancements diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index 94ec9fec2..238b488ac 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -1,4 +1,3 @@ -from datetime import date from typing import TYPE_CHECKING, Annotated import strawberry @@ -62,9 +61,9 @@ class CircuitTerminationFilter( upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - xconnect_id: StrFilterLookup[str] | None = strawberry_django.filter_field() - pp_info: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + xconnect_id: StrFilterLookup | None = strawberry_django.filter_field() + pp_info: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() # Cached relations _provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( @@ -92,7 +91,7 @@ class CircuitFilter( TenancyFilterMixin, PrimaryModelFilter ): - cid: StrFilterLookup[str] | None = strawberry_django.filter_field() + cid: StrFilterLookup | None = strawberry_django.filter_field() provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -108,8 +107,8 @@ class CircuitFilter( status: BaseFilterLookup[Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - install_date: DateFilterLookup[date] | None = strawberry_django.filter_field() - termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field() + install_date: DateFilterLookup | None = strawberry_django.filter_field() + termination_date: DateFilterLookup | None = strawberry_django.filter_field() commit_rate: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -145,8 +144,8 @@ class CircuitGroupAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, Cha @strawberry_django.filter_type(models.Provider, lookups=True) class ProviderFilter(ContactFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() circuits: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -159,18 +158,18 @@ class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilter): strawberry_django.filter_field() ) provider_id: ID | None = strawberry_django.filter_field() - account: StrFilterLookup[str] | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + account: StrFilterLookup | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.ProviderNetwork, lookups=True) class ProviderNetworkFilter(PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() ) provider_id: ID | None = strawberry_django.filter_field() - service_id: StrFilterLookup[str] | None = strawberry_django.filter_field() + service_id: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.VirtualCircuitType, lookups=True) @@ -180,7 +179,7 @@ class VirtualCircuitTypeFilter(CircuitTypeFilterMixin, OrganizationalModelFilter @strawberry_django.filter_type(models.VirtualCircuit, lookups=True) class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilter): - cid: StrFilterLookup[str] | None = strawberry_django.filter_field() + cid: StrFilterLookup | None = strawberry_django.filter_field() provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -218,4 +217,4 @@ class VirtualCircuitTerminationFilter(CustomFieldsFilterMixin, TagsFilterMixin, strawberry_django.filter_field() ) interface_id: ID | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() diff --git a/netbox/core/api/schema.py b/netbox/core/api/schema.py index 5ceb3c61d..4c63296d3 100644 --- a/netbox/core/api/schema.py +++ b/netbox/core/api/schema.py @@ -14,7 +14,7 @@ from drf_spectacular.plumbing import ( get_doc, ) from drf_spectacular.types import OpenApiTypes -from drf_spectacular.utils import Direction +from drf_spectacular.utils import Direction, OpenApiParameter from netbox.api.fields import ChoiceField from netbox.api.serializers import WritableNestedSerializer @@ -138,14 +138,30 @@ class NetBoxAutoSchema(AutoSchema): return super().get_operation_id() def get_request_serializer(self) -> typing.Any: - # bulk operations should specify a list serializer = super().get_request_serializer() + # Bulk update/partial-update has a special request shape: a list of + # writable objects plus a required `id` field. The normal writable + # serializer omits `id` because it is read-only, so don't use the generic + # bulk handling for these actions. + action = getattr(self.view, 'action', None) + if action in ('bulk_update', 'bulk_partial_update'): + get_bulk_update_request_serializer = getattr( + self.view, + 'get_bulk_update_request_serializer', + None, + ) + if get_bulk_update_request_serializer is not None: + return get_bulk_update_request_serializer( + partial=(action == 'bulk_partial_update' or self.method == 'PATCH') + ) + + # Bulk creates/deletes should specify a list. if self.is_bulk_action: return type(serializer)(many=True) - # handle mapping for Writable serializers - adapted from dansheps original code - # for drf-yasg + # handle mapping for Writable serializers - adapted from dansheps original + # code for drf-yasg. if serializer is not None and self.method in WRITABLE_ACTIONS: writable_class = self.get_writable_class(serializer) if writable_class is not None: @@ -258,6 +274,37 @@ class NetBoxAutoSchema(AutoSchema): writable_class = self.writable_serializers[type(serializer)] return writable_class + def get_override_parameters(self): + params = super().get_override_parameters() + # Expose the ?fields, ?omit, and ?brief query parameters supported by NetBoxModelViewSet + # for all non-bulk GET operations (both list and detail). + if not self.is_bulk_action and self.method == 'GET': + params = list(params) + [ + OpenApiParameter( + name='fields', + location=OpenApiParameter.QUERY, + required=False, + type=OpenApiTypes.STR, + description='Comma-separated list of fields to include in the response. Example: `fields=id,name`.', + ), + OpenApiParameter( + name='omit', + location=OpenApiParameter.QUERY, + required=False, + type=OpenApiTypes.STR, + description='Comma-separated list of fields to exclude from the response. ' + 'Example: `omit=description,tags`.', + ), + OpenApiParameter( + name='brief', + location=OpenApiParameter.QUERY, + required=False, + type=OpenApiTypes.BOOL, + description='Return only brief fields for each object.', + ), + ] + return params + def get_filter_backends(self): # bulk operations don't have filter params if self.is_bulk_action: diff --git a/netbox/core/apps.py b/netbox/core/apps.py index 86ed763b5..27f457607 100644 --- a/netbox/core/apps.py +++ b/netbox/core/apps.py @@ -22,7 +22,7 @@ class CoreConfig(AppConfig): def ready(self): from core.api import schema # noqa: F401 - from core.checks import check_duplicate_indexes, check_postgresql_version # noqa: F401 + from core.checks import check_duplicate_indexes, check_postgresql_version, check_redis_version # noqa: F401 from netbox import context_managers # noqa: F401 from netbox.models.features import register_models diff --git a/netbox/core/checks.py b/netbox/core/checks.py index 90300bdd3..d452641c7 100644 --- a/netbox/core/checks.py +++ b/netbox/core/checks.py @@ -1,4 +1,5 @@ from django.apps import apps +from django.core.cache import cache from django.core.checks import Error, Tags, Warning, register from django.db import connection from django.db.models import Index, UniqueConstraint @@ -6,6 +7,7 @@ from django.db.models import Index, UniqueConstraint __all__ = ( 'check_duplicate_indexes', 'check_postgresql_version', + 'check_redis_version', ) @@ -67,3 +69,27 @@ def check_postgresql_version(app_configs, **kwargs): except Exception: pass return warnings + + +@register(Tags.caches) +def check_redis_version(app_configs, **kwargs): + """ + Warn if the Redis version is less than 6.0, as support for Redis older than 6.0 + will be removed in NetBox v4.7. + """ + warnings = [] + try: + client = cache.client.get_client() + redis_version = tuple(int(x) for x in client.info()['redis_version'].split('.')) + if redis_version < (6, 0): + warnings.append( + Warning( + f'Support for Redis {".".join(str(x) for x in redis_version)} is deprecated and will be ' + f'removed in NetBox v4.7.', + hint='Please upgrade to Redis 6.0 or later.', + id='netbox.W002', + ) + ) + except Exception: + pass + return warnings diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index 5629e7784..0e097fbcc 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -115,15 +115,6 @@ class ManagedFileForm(SyncedDataMixin, NetBoxModelForm): return self.cleaned_data - def save(self, *args, **kwargs): - # If a file was uploaded, save it to disk - if self.cleaned_data['upload_file']: - self.instance.file_path = self.cleaned_data['upload_file'].name - with open(self.instance.full_path, 'wb+') as new_file: - new_file.write(self.cleaned_data['upload_file'].read()) - - return super().save(*args, **kwargs) - class ConfigFormMetaclass(forms.models.ModelFormMetaclass): diff --git a/netbox/core/graphql/filter_mixins.py b/netbox/core/graphql/filter_mixins.py index c094ecb24..96c4b7c29 100644 --- a/netbox/core/graphql/filter_mixins.py +++ b/netbox/core/graphql/filter_mixins.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from datetime import datetime from typing import TYPE_CHECKING, Annotated import strawberry @@ -20,5 +19,5 @@ class ChangeLoggingMixin: changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) - created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() - last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + created: DatetimeFilterLookup | None = strawberry_django.filter_field() + last_updated: DatetimeFilterLookup | None = strawberry_django.filter_field() diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index 3424c5737..05f4803e4 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -1,4 +1,3 @@ -from datetime import datetime from typing import TYPE_CHECKING, Annotated import strawberry @@ -26,33 +25,33 @@ __all__ = ( @strawberry_django.filter_type(models.DataFile, lookups=True) class DataFileFilter(BaseModelFilter): - created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() - last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + created: DatetimeFilterLookup | None = strawberry_django.filter_field() + last_updated: DatetimeFilterLookup | None = strawberry_django.filter_field() source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) source_id: ID | None = strawberry_django.filter_field() - path: StrFilterLookup[str] | None = strawberry_django.filter_field() + path: StrFilterLookup | None = strawberry_django.filter_field() size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - hash: StrFilterLookup[str] | None = strawberry_django.filter_field() + hash: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.DataSource, lookups=True) class DataSourceFilter(PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - type: StrFilterLookup[str] | None = strawberry_django.filter_field() - source_url: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + type: StrFilterLookup | None = strawberry_django.filter_field() + source_url: StrFilterLookup | None = strawberry_django.filter_field() status: ( BaseFilterLookup[Annotated['DataSourceStatusEnum', strawberry.lazy('core.graphql.enums')]] | None ) = strawberry_django.filter_field() enabled: FilterLookup[bool] | None = strawberry_django.filter_field() - ignore_rules: StrFilterLookup[str] | None = strawberry_django.filter_field() + ignore_rules: StrFilterLookup | None = strawberry_django.filter_field() parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - last_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + last_synced: DatetimeFilterLookup | None = strawberry_django.filter_field() datafiles: Annotated['DataFileFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -60,10 +59,10 @@ class DataSourceFilter(PrimaryModelFilter): @strawberry_django.filter_type(models.ObjectChange, lookups=True) class ObjectChangeFilter(BaseModelFilter): - time: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + time: DatetimeFilterLookup | None = strawberry_django.filter_field() user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() - user_name: StrFilterLookup[str] | None = strawberry_django.filter_field() - request_id: StrFilterLookup[str] | None = strawberry_django.filter_field() + user_name: StrFilterLookup | None = strawberry_django.filter_field() + request_id: StrFilterLookup | None = strawberry_django.filter_field() action: ( BaseFilterLookup[Annotated['ObjectChangeActionEnum', strawberry.lazy('core.graphql.enums')]] | None ) = strawberry_django.filter_field() @@ -76,7 +75,7 @@ class ObjectChangeFilter(BaseModelFilter): strawberry_django.filter_field() ) related_object_id: ID | None = strawberry_django.filter_field() - object_repr: StrFilterLookup[str] | None = strawberry_django.filter_field() + object_repr: StrFilterLookup | None = strawberry_django.filter_field() prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -87,5 +86,5 @@ class ObjectChangeFilter(BaseModelFilter): @strawberry_django.filter_type(DjangoContentType, lookups=True) class ContentTypeFilter(BaseModelFilter): - app_label: StrFilterLookup[str] | None = strawberry_django.filter_field() - model: StrFilterLookup[str] | None = strawberry_django.filter_field() + app_label: StrFilterLookup | None = strawberry_django.filter_field() + model: StrFilterLookup | None = strawberry_django.filter_field() diff --git a/netbox/core/tests/test_api.py b/netbox/core/tests/test_api.py index 2a159f6bd..332169f2c 100644 --- a/netbox/core/tests/test_api.py +++ b/netbox/core/tests/test_api.py @@ -12,7 +12,7 @@ from rq.registry import FailedJobRegistry, StartedJobRegistry from users.constants import TOKEN_PREFIX from users.models import Token -from utilities.testing import APITestCase, APIViewTestCases, TestCase +from utilities.testing import APITestCase, APIViewTestCases, GraphQLQueryTest, TestCase from utilities.testing.mixins import RQQueueTestMixin from utilities.testing.utils import disable_logging @@ -39,12 +39,49 @@ class DataSourceTestCase(APIViewTestCases.APIViewTestCase): @classmethod def setUpTestData(cls): data_sources = ( - DataSource(name='Data Source 1', type='local', source_url='file:///var/tmp/source1/'), + DataSource( + name='Data Source 1', type='local', source_url='file:///var/tmp/source1/', + parameters={ + 'sync_date': '2024-01-01', + 'sync_datetime': '2024-01-01T12:30:00+00:00', + 'sync_time': '12:30:00', + }, + ), DataSource(name='Data Source 2', type='local', source_url='file:///var/tmp/source2/'), DataSource(name='Data Source 3', type='local', source_url='file:///var/tmp/source3/'), ) DataSource.objects.bulk_create(data_sources) + cls.graphql_query_tests = ( + GraphQLQueryTest( + name='parameters_json_date_lookup', + query=( + '{ data_source_list(filters: {parameters: ' + '{path: "sync_date", lookup: {date_lookup: {exact: "2024-01-01"}}}}) ' + '{ id } }' + ), + assert_result=cls.assert_only_source_1, + ), + GraphQLQueryTest( + name='parameters_json_datetime_lookup', + query=( + '{ data_source_list(filters: {parameters: ' + '{path: "sync_datetime", lookup: {datetime_lookup: {exact: "2024-01-01T12:30:00+00:00"}}}}) ' + '{ id } }' + ), + assert_result=cls.assert_only_source_1, + ), + GraphQLQueryTest( + name='parameters_json_time_lookup', + query=( + '{ data_source_list(filters: {parameters: ' + '{path: "sync_time", lookup: {time_lookup: {exact: "12:30:00"}}}}) ' + '{ id } }' + ), + assert_result=cls.assert_only_source_1, + ), + ) + cls.create_data = [ { 'name': 'Data Source 4', @@ -63,6 +100,11 @@ class DataSourceTestCase(APIViewTestCases.APIViewTestCase): }, ] + def assert_only_source_1(self, data): + """The JSON lookup returns exactly the source carrying the matching value.""" + ids = sorted(result['id'] for result in data['data_source_list']) + self.assertEqual(ids, [str(DataSource.objects.get(name='Data Source 1').pk)]) + class DataFileTestCase( APIViewTestCases.GetObjectViewTestCase, @@ -279,9 +321,8 @@ class BackgroundTaskTestCase(RQQueueTestMixin, TestCase): # Enqueue & run a job that will fail queue = get_queue('default') job = queue.enqueue(self.dummy_job_failing) - worker = get_worker('default') with disable_logging(): - worker.work(burst=True) + self.run_rq_jobs('default') self.assertTrue(job.is_failed) url = reverse('core-api:rqtask-requeue', args=[job.id]) diff --git a/netbox/core/tests/test_views.py b/netbox/core/tests/test_views.py index b763eaed2..dfa2c9ed8 100644 --- a/netbox/core/tests/test_views.py +++ b/netbox/core/tests/test_views.py @@ -1,7 +1,7 @@ import json import urllib.parse import uuid -from datetime import datetime +from datetime import UTC, datetime from django.contrib.contenttypes.models import ContentType from django.urls import reverse @@ -152,6 +152,77 @@ class JobTestCase( ) +class JobLogViewTestCase(TestCase): + user_permissions = ( + 'core.view_job', + ) + + @classmethod + def setUpTestData(cls): + cls.job = Job.objects.create( + name='Test Job', + job_id=uuid.uuid4(), + ) + cls.job.log_entries = [ + { + 'level': 'info', + 'message': f'log line {i}', + 'timestamp': datetime(2026, 1, 1, tzinfo=UTC), + } + for i in range(120) + ] + cls.job.save() + + def setUp(self): + super().setUp() + # UserConfig.set() mutates self.data in place, which can mutate DEFAULT_USER_PREFERENCES + # (the signal in users/signals.py initializes data with a shared reference). Assign a + # fresh literal instead. Pin per_page so page-boundary assertions don't depend on PAGINATE_COUNT. + self.user.config.data = {'pagination': {'per_page': 50}} + self.user.config.save() + + def test_log_page_renders_table_inline(self): + """The full page renders the first log page inside an HTMX container.""" + url = reverse('core:job_log', kwargs={'pk': self.job.pk}) + response = self.client.get(url) + self.assertHttpStatus(response, 200) + self.assertContains(response, 'htmx-container') + self.assertContains(response, 'log line 0') + self.assertContains(response, 'Showing 1-50 of 120') + + def test_log_page_table_is_embedded(self): + """The embedded table never pushes page/per_page into the browser URL.""" + url = reverse('core:job_log', kwargs={'pk': self.job.pk}) + response = self.client.get(url) + self.assertHttpStatus(response, 200) + self.assertNotContains(response, 'hx-push-url="true"') + + def test_log_table_htmx_renders_partial(self): + """An HTMX request returns the paginated table partial.""" + url = reverse('core:job_log', kwargs={'pk': self.job.pk}) + response = self.client.get(url, headers={'hx-request': 'true'}) + self.assertHttpStatus(response, 200) + self.assertContains(response, 'log line 0') + self.assertContains(response, 'Showing 1-50 of 120') + self.assertContains(response, 'Per Page') + + def test_log_table_htmx_page_navigation(self): + """`?page=2` advances the embedded table to the second page.""" + url = reverse('core:job_log', kwargs={'pk': self.job.pk}) + response = self.client.get(f'{url}?page=2', headers={'hx-request': 'true'}) + self.assertHttpStatus(response, 200) + self.assertContains(response, 'log line 50') + self.assertNotContains(response, 'log line 49') + + def test_log_table_htmx_per_page(self): + """`?per_page=100` widens the embedded table page size.""" + url = reverse('core:job_log', kwargs={'pk': self.job.pk}) + response = self.client.get(f'{url}?per_page=100', headers={'hx-request': 'true'}) + self.assertHttpStatus(response, 200) + self.assertContains(response, 'log line 99') + self.assertNotContains(response, 'log line 100') + + # TODO: Convert to StandardTestCases.Views class ObjectChangeTestCase(TestCase): user_permissions = ( @@ -315,9 +386,8 @@ class BackgroundTaskTestCase(RQQueueTestMixin, TestCase): # Enqueue & run a job that will fail job = queue.enqueue(self.dummy_job_failing) - worker = get_worker('default') with disable_logging(): - worker.work(burst=True) + self.run_rq_jobs('default') self.assertTrue(job.is_failed) # Re-enqueue the failed job and check that its status has been reset diff --git a/netbox/core/views.py b/netbox/core/views.py index 760346b3d..78059ce2d 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -39,7 +39,6 @@ from netbox.plugins.utils import get_installed_plugins from netbox.ui import layout from netbox.ui.panels import ( CommentsPanel, - ContextTablePanel, JSONPanel, ObjectsTablePanel, PluginContentPanel, @@ -269,7 +268,7 @@ class JobLogView(generic.ObjectView): layout = layout.Layout( layout.Row( layout.Column( - ContextTablePanel('table', title=_('Log Entries')), + TemplatePanel('core/job/log_entries.html', title=_('Log Entries')), PluginContentPanel('left_page'), ), ), @@ -280,13 +279,27 @@ class JobLogView(generic.ObjectView): ), ) - def get_extra_context(self, request, instance): + def get_table(self, request, instance): table = JobLogEntryTable(instance.log_entries) + table.embedded = True + table.htmx_url = reverse('core:job_log', kwargs={'pk': instance.pk}) table.configure(request) + return table + + def get_extra_context(self, request, instance): return { - 'table': table, + 'table': self.get_table(request, instance), } + def get(self, request, **kwargs): + if htmx_partial(request): + instance = self.get_object(**kwargs) + return render(request, 'htmx/table.html', { + 'object': instance, + 'table': self.get_table(request, instance), + }) + return super().get(request, **kwargs) + @register_model_view(Job, 'delete') class JobDeleteView(generic.ObjectDeleteView): diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index 514d4aedf..58011b5f7 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -1637,6 +1637,10 @@ class PortTypeChoices(ChoiceSet): TYPE_LC_PC = 'lc-pc' TYPE_LC_UPC = 'lc-upc' TYPE_LC_APC = 'lc-apc' + TYPE_MU = 'mu' + TYPE_MU_PC = 'mu-pc' + TYPE_MU_UPC = 'mu-upc' + TYPE_MU_APC = 'mu-apc' TYPE_MTRJ = 'mtrj' TYPE_MPO = 'mpo' TYPE_LSH = 'lsh' @@ -1700,6 +1704,10 @@ class PortTypeChoices(ChoiceSet): (TYPE_LC_PC, 'LC/PC'), (TYPE_LC_UPC, 'LC/UPC'), (TYPE_LC_APC, 'LC/APC'), + (TYPE_MU, 'MU'), + (TYPE_MU_PC, 'MU/PC'), + (TYPE_MU_UPC, 'MU/UPC'), + (TYPE_MU_APC, 'MU/APC'), (TYPE_LSH, 'LSH'), (TYPE_LSH_PC, 'LSH/PC'), (TYPE_LSH_UPC, 'LSH/UPC'), diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 6f3197919..f756672fe 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -1583,7 +1583,8 @@ class CableImportForm(PrimaryModelImportForm): :param side: 'a' or 'b' """ - assert side in 'ab', f"Invalid side designation: {side}" + if side not in ('a', 'b'): + raise ValueError(_("Invalid side designation: {side}").format(side=side)) device = self.cleaned_data.get(f'side_{side}_device') power_panel = self.cleaned_data.get(f'side_{side}_power_panel') diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py index 07fa34616..fb5a21a0b 100644 --- a/netbox/dcim/graphql/filter_mixins.py +++ b/netbox/dcim/graphql/filter_mixins.py @@ -66,9 +66,9 @@ class ComponentModelFilterMixin: ) device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() device_id: ID | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - label: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + label: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() @dataclass @@ -96,9 +96,9 @@ class ComponentTemplateFilterMixin: strawberry_django.filter_field() ) device_type_id: ID | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - label: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + label: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() @dataclass diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 02fb3ef64..09ba07acd 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -116,7 +116,7 @@ __all__ = ( @strawberry_django.filter_type(models.CableBundle, lookups=True) class CableBundleFilter(PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.Cable, lookups=True) @@ -127,7 +127,7 @@ class CableFilter(TenancyFilterMixin, PrimaryModelFilter): status: BaseFilterLookup[Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - label: StrFilterLookup[str] | None = strawberry_django.filter_field() + label: StrFilterLookup | None = strawberry_django.filter_field() color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -223,9 +223,9 @@ class DeviceFilter( platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - serial: StrFilterLookup[str] | None = strawberry_django.filter_field() - asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + serial: StrFilterLookup | None = strawberry_django.filter_field() + asset_tag: StrFilterLookup | None = strawberry_django.filter_field() site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() site_id: ID | None = strawberry_django.filter_field() location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( @@ -353,7 +353,7 @@ class InventoryItemTemplateFilter(ComponentTemplateFilterMixin, ChangeLoggedMode strawberry_django.filter_field() ) manufacturer_id: ID | None = strawberry_django.filter_field() - part_id: StrFilterLookup[str] | None = strawberry_django.filter_field() + part_id: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.DeviceRole, lookups=True) @@ -370,13 +370,13 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryMod strawberry_django.filter_field() ) manufacturer_id: ID | None = strawberry_django.filter_field() - model: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() + model: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() default_platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) default_platform_id: ID | None = strawberry_django.filter_field() - part_number: StrFilterLookup[str] | None = strawberry_django.filter_field() + part_number: StrFilterLookup | None = strawberry_django.filter_field() instances: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -493,7 +493,7 @@ class PortTemplateMappingFilter(BaseModelFilter): @strawberry_django.filter_type(models.MACAddress, lookups=True) class MACAddressFilter(PrimaryModelFilter): - mac_address: StrFilterLookup[str] | None = strawberry_django.filter_field() + mac_address: StrFilterLookup | None = strawberry_django.filter_field() assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -539,7 +539,7 @@ class InterfaceFilter( duplex: BaseFilterLookup[Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - wwn: StrFilterLookup[str] | None = strawberry_django.filter_field() + wwn: StrFilterLookup | None = strawberry_django.filter_field() parent: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -659,9 +659,9 @@ class InventoryItemFilter(ComponentModelFilterMixin, NetBoxModelFilter): strawberry_django.filter_field() ) manufacturer_id: ID | None = strawberry_django.filter_field() - part_id: StrFilterLookup[str] | None = strawberry_django.filter_field() - serial: StrFilterLookup[str] | None = strawberry_django.filter_field() - asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field() + part_id: StrFilterLookup | None = strawberry_django.filter_field() + serial: StrFilterLookup | None = strawberry_django.filter_field() + asset_tag: StrFilterLookup | None = strawberry_django.filter_field() discovered: FilterLookup[bool] | None = strawberry_django.filter_field() @@ -679,7 +679,7 @@ class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilt status: BaseFilterLookup[Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - facility: StrFilterLookup[str] | None = strawberry_django.filter_field() + facility: StrFilterLookup | None = strawberry_django.filter_field() prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -708,8 +708,8 @@ class ModuleFilter(ConfigContextFilterMixin, PrimaryModelFilter): status: BaseFilterLookup[Annotated['ModuleStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - serial: StrFilterLookup[str] | None = strawberry_django.filter_field() - asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field() + serial: StrFilterLookup | None = strawberry_django.filter_field() + asset_tag: StrFilterLookup | None = strawberry_django.filter_field() consoleports: Annotated['ConsolePortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field(name='console_ports') ) @@ -748,19 +748,19 @@ class ModuleBayFilter(ModularComponentFilterMixin, NetBoxModelFilter): strawberry_django.filter_field() ) parent_id: ID | None = strawberry_django.filter_field() - position: StrFilterLookup[str] | None = strawberry_django.filter_field() + position: StrFilterLookup | 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() + position: StrFilterLookup | None = strawberry_django.filter_field() enabled: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True) class ModuleTypeProfileFilter(PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.ModuleType, lookups=True) @@ -773,8 +773,8 @@ class ModuleTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryMod strawberry_django.filter_field() ) profile_id: ID | None = strawberry_django.filter_field() - model: StrFilterLookup[str] | None = strawberry_django.filter_field() - part_number: StrFilterLookup[str] | None = strawberry_django.filter_field() + model: StrFilterLookup | None = strawberry_django.filter_field() + part_number: StrFilterLookup | None = strawberry_django.filter_field() instances: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -831,7 +831,7 @@ class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryM power_panel_id: ID | None = strawberry_django.filter_field() rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() rack_id: ID | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() status: BaseFilterLookup[Annotated['PowerFeedStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -902,7 +902,7 @@ class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryMo location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.PowerPort, lookups=True) @@ -940,8 +940,8 @@ class RackTypeFilter(ImageAttachmentFilterMixin, RackFilterMixin, WeightFilterMi strawberry_django.filter_field() ) manufacturer_id: ID | None = strawberry_django.filter_field() - model: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() + model: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() racks: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() rack_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() @@ -962,8 +962,8 @@ class RackFilter( strawberry_django.filter_field() ) rack_type_id: ID | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - facility_id: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + facility_id: StrFilterLookup | None = strawberry_django.filter_field() site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() site_id: ID | None = strawberry_django.filter_field() location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( @@ -981,8 +981,8 @@ class RackFilter( ) role: Annotated['RackRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() role_id: ID | None = strawberry_django.filter_field() - serial: StrFilterLookup[str] | None = strawberry_django.filter_field() - asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field() + serial: StrFilterLookup | None = strawberry_django.filter_field() + asset_tag: StrFilterLookup | None = strawberry_django.filter_field() airflow: BaseFilterLookup[Annotated['RackAirflowEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -1006,7 +1006,7 @@ class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter): 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() + description: StrFilterLookup | None = strawberry_django.filter_field() status: BaseFilterLookup[Annotated['RackReservationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -1057,8 +1057,8 @@ class RegionFilter(ContactFilterMixin, NestedGroupModelFilter): @strawberry_django.filter_type(models.Site, lookups=True) class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() status: BaseFilterLookup[Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -1072,11 +1072,11 @@ class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMi group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - facility: StrFilterLookup[str] | None = strawberry_django.filter_field() + facility: StrFilterLookup | None = strawberry_django.filter_field() asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() - time_zone: StrFilterLookup[str] | None = strawberry_django.filter_field() - physical_address: StrFilterLookup[str] | None = strawberry_django.filter_field() - shipping_address: StrFilterLookup[str] | None = strawberry_django.filter_field() + time_zone: StrFilterLookup | None = strawberry_django.filter_field() + physical_address: StrFilterLookup | None = strawberry_django.filter_field() + shipping_address: StrFilterLookup | None = strawberry_django.filter_field() latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -1105,8 +1105,8 @@ class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilter): class VirtualChassisFilter(PrimaryModelFilter): master: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() master_id: ID | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - domain: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + domain: StrFilterLookup | None = strawberry_django.filter_field() members: ( Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None ) = strawberry_django.filter_field() @@ -1117,7 +1117,7 @@ class VirtualChassisFilter(PrimaryModelFilter): class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilter): device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() device_id: ID | None = strawberry_django.filter_field() - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() status: ( BaseFilterLookup[Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None ) = ( @@ -1134,7 +1134,7 @@ class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilter): strawberry_django.filter_field() ) primary_ip6_id: ID | None = strawberry_django.filter_field() - comments: StrFilterLookup[str] | None = strawberry_django.filter_field() + comments: StrFilterLookup | None = strawberry_django.filter_field() interfaces: ( Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None ) = strawberry_django.filter_field() diff --git a/netbox/dcim/migrations/0239_device__config_context_data.py b/netbox/dcim/migrations/0240_device__config_context_data.py similarity index 93% rename from netbox/dcim/migrations/0239_device__config_context_data.py rename to netbox/dcim/migrations/0240_device__config_context_data.py index 39d198784..4c523deb8 100644 --- a/netbox/dcim/migrations/0239_device__config_context_data.py +++ b/netbox/dcim/migrations/0240_device__config_context_data.py @@ -3,7 +3,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('dcim', '0238_ltree_paths'), + ('dcim', '0239_denormalization_triggers'), ] operations = [ diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 218300a53..d1c70d942 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -675,7 +675,10 @@ class CableTermination(ChangeLoggedModel): Cache objects related to the termination (e.g. device, rack, site) directly on the object to enable efficient filtering. """ - assert self.termination is not None + if self.termination is None: + raise ValueError( + _("Invalid cable termination: the assigned termination object does not exist.") + ) # Device components if getattr(self.termination, 'device', None): diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index ce2e376bf..af008ceca 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -384,12 +384,27 @@ class PathEndpoint(models.Model): a stale in-memory `_path` relation while the database already points to a different CablePath (or to no path at all). - If the cached relation points to a CablePath that has just been - deleted, refresh only the `_path` field from the database and retry. - This keeps the fix cheap and narrowly scoped to the denormalized FK. + Two stale cases are repaired by refreshing only the `_path` field + from the database: + + 1. The endpoint is linked (by cable or wireless link) but `_path` is + unset, because the instance was loaded before its path was traced + (e.g. while queued for event serialization during link creation). + 2. The cached relation points to a CablePath row that has just been + deleted. + + Repairing case 1 costs one query per access for a linked endpoint + whose path is genuinely absent in the database. That state is + transient outside of tracing failures, so no result caching is + attempted here. """ if self._path_id is None: - return None + has_link = self.cable_id is not None or getattr(self, 'wireless_link_id', None) is not None + if self.pk and has_link: + self.refresh_from_db(fields=['_path']) + + if self._path_id is None: + return None try: return self._path diff --git a/netbox/dcim/models/modules.py b/netbox/dcim/models/modules.py index 100293bd6..05fd74495 100644 --- a/netbox/dcim/models/modules.py +++ b/netbox/dcim/models/modules.py @@ -325,9 +325,20 @@ class Module(TrackingModelMixin, PrimaryModel): def save(self, *args, **kwargs): is_new = self.pk is None + old_module_bay_id = None + + if not is_new: + old_module_bay_id = Module.objects.filter(pk=self.pk).values_list( + 'module_bay_id', flat=True + ).first() super().save(*args, **kwargs) + if old_module_bay_id is not None and old_module_bay_id != self.module_bay_id: + for child_bay in self.modulebays.select_related('module__module_bay'): + child_bay.snapshot() + child_bay.save() + adopt_components = getattr(self, '_adopt_components', False) disable_replication = getattr(self, '_disable_replication', False) diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 7a226e0c6..1b7eb1d32 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -638,9 +638,6 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, TrackingModelMixin, RackBase): return elevation.render(face) - def get_0u_devices(self): - return self.devices.filter(position=0) - def get_utilization(self): """ Determine the utilization rate of the rack and return it as a percentage. Occupied and reserved units both count diff --git a/netbox/dcim/tests/query_counts.json b/netbox/dcim/tests/query_counts.json index 1d7cdda72..7861ee6b7 100644 --- a/netbox/dcim/tests/query_counts.json +++ b/netbox/dcim/tests/query_counts.json @@ -72,7 +72,7 @@ "rearporttemplate:api_list_objects": 12, "region:api_list_objects": 13, "region:list_objects_with_permission": 20, - "site:api_list_objects": 16, + "site:api_list_objects": 17, "site:list_objects_with_permission": 22, "sitegroup:api_list_objects": 13, "sitegroup:list_objects_with_permission": 20, diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index f6b8a4726..42e550a5c 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -20,6 +20,8 @@ from users.models import ObjectPermission, Token, User from utilities.testing import ( APITestCase, APIViewTestCases, + GraphQLFilterTest, + GraphQLQueryTest, create_test_device, create_test_nat_ip_pair, disable_logging, @@ -146,6 +148,19 @@ class SiteTestCase(APIViewTestCases.APIViewTestCase): bulk_update_data = { 'status': 'planned', } + graphql_filter_tests = ( + GraphQLFilterTest( + name='tenant__name__exact', + filters='tenant: {name: {exact: "Tenant 1"}}', + expected=lambda qs: qs.filter(tenant__name='Tenant 1'), + permissions=('tenancy.view_tenant',), + ), + ) + + def assert_nested_locations_active(self, data): + site_data = data.get('site') or {} + location_names = sorted(location['name'] for location in site_data.get('locations', [])) + self.assertEqual(location_names, ['Site1 Active A', 'Site1 Active B']) @classmethod def setUpTestData(cls): @@ -160,15 +175,32 @@ class SiteTestCase(APIViewTestCases.APIViewTestCase): SiteGroup.objects.create(name='Site Group 2', slug='site-group-2'), ) + tenant = Tenant.objects.create(name='Tenant 1', slug='tenant-1') + + # Site 1's tenant activates the dynamic tenant prefetch (+1 in api_list_objects baseline). sites = ( - Site(region=regions[0], group=groups[0], name='Site 1', slug='site-1'), + Site(region=regions[0], group=groups[0], tenant=tenant, name='Site 1', slug='site-1'), Site(region=regions[0], group=groups[0], name='Site 2', slug='site-2'), Site(region=regions[0], group=groups[0], name='Site 3', slug='site-3'), ) Site.objects.bulk_create(sites) + nested_site = Site.objects.get(slug='site-1') + cls.nested_site_pk = nested_site.pk + Location.objects.create( + site=nested_site, name='Site1 Active A', slug='site1-active-a', + status=LocationStatusChoices.STATUS_ACTIVE, + ) + Location.objects.create( + site=nested_site, name='Site1 Active B', slug='site1-active-b', + status=LocationStatusChoices.STATUS_ACTIVE, + ) + Location.objects.create( + site=nested_site, name='Site1 Planned', slug='site1-planned', + status=LocationStatusChoices.STATUS_PLANNED, + ) + rir = RIR.objects.create(name='RFC 6996', is_private=True) - tenant = Tenant.objects.create(name='Tenant 1', slug='tenant-1') asns = [ ASN(asn=65000 + i, rir=rir) for i in range(8) @@ -203,6 +235,19 @@ class SiteTestCase(APIViewTestCases.APIViewTestCase): }, ] + cls.graphql_query_tests = ( + GraphQLQueryTest( + name='nested_locations_by_status', + query=( + '{ site(id: ' + str(cls.nested_site_pk) + ') { ' + 'locations(filters: {status: {exact: STATUS_ACTIVE}}) { name } ' + '} }' + ), + assert_result=cls.assert_nested_locations_active, + permissions=('dcim.view_location',), + ), + ) + def test_add_tags(self): """ Add tags to an existing object via the add_tags field. @@ -427,6 +472,16 @@ class LocationTestCase(APIViewTestCases.APIViewTestCase): 'description': 'New description', } user_permissions = ('dcim.view_site',) + graphql_filter_tests = ( + GraphQLFilterTest( + name='status__in_list', + filters='status: {in_list: [STATUS_PLANNED, STATUS_STAGING]}', + expected=lambda qs: qs.filter(status__in=[ + LocationStatusChoices.STATUS_PLANNED, + LocationStatusChoices.STATUS_STAGING, + ]), + ), + ) @classmethod def setUpTestData(cls): @@ -476,6 +531,20 @@ class LocationTestCase(APIViewTestCases.APIViewTestCase): parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE, ) + Location.objects.create( + site=sites[0], + name='GraphQL Planned Location', + slug='graphql-planned-location', + parent=parent_locations[0], + status=LocationStatusChoices.STATUS_PLANNED, + ) + Location.objects.create( + site=sites[0], + name='GraphQL Staging Location', + slug='graphql-staging-location', + parent=parent_locations[0], + status=LocationStatusChoices.STATUS_STAGING, + ) cls.create_data = [ { diff --git a/netbox/dcim/tests/test_forms.py b/netbox/dcim/tests/test_forms.py index 3fe568384..2bb0d241d 100644 --- a/netbox/dcim/tests/test_forms.py +++ b/netbox/dcim/tests/test_forms.py @@ -503,6 +503,15 @@ class InterfaceTestCase(TestCase): self.assertNotIn('qinq_svlan', form.cleaned_data.keys()) +class CableTestCase(TestCase): + + def test_invalid_side_designation_raises_value_error(self): + """_clean_side rejects a side other than 'a' or 'b' with ValueError.""" + form = CableImportForm.__new__(CableImportForm) + with self.assertRaisesMessage(ValueError, "Invalid side designation: c"): + form._clean_side('c') + + class SiteFormTestCase(TestCase): """ Tests for M2MAddRemoveFields using Site ASN assignments as the test case. diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index aa7c43b15..db65164cd 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -1,4 +1,5 @@ from django.core.exceptions import ValidationError +from django.db.models.signals import post_save from django.test import TestCase, tag from circuits.models import * @@ -1187,6 +1188,105 @@ class ModuleBayTestCase(TestCase): # tree: its path is now a strict descendant of host_bay's path. self.assertTrue(str(movable_bay.path).startswith(f'{host_bay.path}.')) + @tag('regression') # #22251 + def test_moving_module_reparents_child_module_bays(self): + """ + When a module is moved to a different module bay, each child ModuleBay + (a bay that belongs to the module) must have its parent updated to the + new host bay. Without the fix the children stay parented to the old bay + even though Module.module_bay_id has changed. + """ + device_type = DeviceType.objects.first() + device_role = DeviceRole.objects.first() + site = Site.objects.first() + device = Device.objects.create( + name='Move Module Device', + device_type=device_type, + role=device_role, + site=site, + ) + bay_a = ModuleBay.objects.create(device=device, name='Bay A') + bay_b = ModuleBay.objects.create(device=device, name='Bay B') + + manufacturer = Manufacturer.objects.first() + module_type = ModuleType.objects.create( + manufacturer=manufacturer, model='Move Module Type' + ) + module = Module.objects.create( + device=device, module_bay=bay_a, module_type=module_type + ) + + child_1 = ModuleBay.objects.create(device=device, module=module, name='Child Bay 1') + child_2 = ModuleBay.objects.create(device=device, module=module, name='Child Bay 2') + self.assertEqual(child_1.parent_id, bay_a.pk) + self.assertEqual(child_2.parent_id, bay_a.pk) + + # Move the module to bay_b. + module.module_bay = bay_b + module.save() + + child_1.refresh_from_db() + child_2.refresh_from_db() + self.assertEqual(child_1.parent_id, bay_b.pk) + self.assertEqual(child_2.parent_id, bay_b.pk) + # Children must be re-rooted under bay_b in the ltree hierarchy. + bay_b.refresh_from_db() + self.assertTrue(str(child_1.path).startswith(f'{bay_b.path}.')) + self.assertTrue(str(child_2.path).startswith(f'{bay_b.path}.')) + + @tag('regression') # #22251 + def test_moving_module_reparents_grandchild_module_bays(self): + """ + When a module is moved, grandchild ModuleBays (bays inside a module + that is itself installed inside a child bay of the moved module) must + also land in the new ltree subtree. The trigger cascade moves subtrees + atomically, so calling save() only on direct children is sufficient — + this test documents and preserves that invariant for future tree-backend + changes. + """ + device_type = DeviceType.objects.first() + device_role = DeviceRole.objects.first() + site = Site.objects.first() + device = Device.objects.create( + name='Grandchild Move Device', + device_type=device_type, + role=device_role, + site=site, + ) + bay_a = ModuleBay.objects.create(device=device, name='Bay A') + bay_b = ModuleBay.objects.create(device=device, name='Bay B') + + manufacturer = Manufacturer.objects.first() + module_type = ModuleType.objects.create( + manufacturer=manufacturer, model='Grandchild Move Type' + ) + # Depth-1: module installed in bay_a, with one child bay. + module_1 = Module.objects.create(device=device, module_bay=bay_a, module_type=module_type) + child_bay = ModuleBay.objects.create(device=device, module=module_1, name='Child Bay') + + # Depth-2: module installed in child_bay, with one grandchild bay. + module_2 = Module.objects.create(device=device, module_bay=child_bay, module_type=module_type) + grandchild_bay = ModuleBay.objects.create(device=device, module=module_2, name='Grandchild Bay') + + self.assertEqual(child_bay.parent_id, bay_a.pk) + self.assertEqual(grandchild_bay.parent_id, child_bay.pk) + bay_a.refresh_from_db() + self.assertTrue(str(grandchild_bay.path).startswith(f'{bay_a.path}.')) + + # Move the top-level module to bay_b. + module_1.module_bay = bay_b + module_1.save() + + child_bay.refresh_from_db() + grandchild_bay.refresh_from_db() + bay_b.refresh_from_db() + + self.assertEqual(child_bay.parent_id, bay_b.pk) + self.assertTrue(str(child_bay.path).startswith(f'{bay_b.path}.')) + # Grandchild's direct parent (child_bay) is unchanged; only tree placement moves. + self.assertEqual(grandchild_bay.parent_id, child_bay.pk) + self.assertTrue(str(grandchild_bay.path).startswith(f'{bay_b.path}.')) + def test_single_module_token(self): device_type = DeviceType.objects.first() device_role = DeviceRole.objects.first() @@ -2066,6 +2166,73 @@ class CableTestCase(TestCase): self.assertIsNone(data['connected_endpoints_type']) self.assertFalse(data['connected_endpoints_reachable']) + @tag('regression') # #21338 + def test_path_refreshes_unset_cablepath_reference(self): + """ + An endpoint instance saved during cable creation, before path tracing, + should resolve its path and connected endpoints. + + The stale-instance preconditions rely on Cable.save() saving each + CableTermination (which re-saves the endpoint) before trace_paths + creates the CablePath records. + """ + device = Device.objects.get(name='TestDevice2') + interface_a = Interface.objects.create(device=device, name='eth2') + interface_b = Interface.objects.create(device=device, name='eth3') + + # Capture the instances handed to the event machinery on save + saved_instances = [] + + def capture(sender, instance, **kwargs): + saved_instances.append(instance) + + post_save.connect(capture, sender=Interface) + try: + Cable(a_terminations=[interface_a], b_terminations=[interface_b]).save() + finally: + post_save.disconnect(capture, sender=Interface) + + self.assertEqual(len(saved_instances), 2) + captured_a = next(i for i in saved_instances if i.pk == interface_a.pk) + captured_b = next(i for i in saved_instances if i.pk == interface_b.pk) + + # The captured instances predate path tracing: cabled, but no path yet + self.assertIsNotNone(captured_a.cable_id) + self.assertIsNone(captured_a._path_id) + self.assertIsNone(captured_b._path_id) + + # The accessor must repair the unset denormalized reference + self.assertIsNotNone(captured_a.path) + self.assertEqual(captured_a.connected_endpoints, [interface_b]) + + # Serialization as performed by the event queue must see the peer + data = serialize_for_event(captured_b) + self.assertEqual([endpoint['id'] for endpoint in data['connected_endpoints']], [interface_a.pk]) + self.assertEqual([peer['id'] for peer in data['link_peers']], [interface_a.pk]) + self.assertTrue(data['connected_endpoints_reachable']) + + def test_path_returns_none_for_unsaved_endpoint(self): + """ + An unsaved endpoint with a link assigned should report no path rather + than attempting a database refresh. + """ + device = Device.objects.get(name='TestDevice1') + cable = Cable.objects.first() + interface = Interface(device=device, name='tmp', cable=cable) + self.assertIsNone(interface.path) + + +class CableTerminationTestCase(TestCase): + + def test_cache_related_objects_requires_resolvable_termination(self): + """cache_related_objects raises ValueError when the termination cannot be resolved.""" + cable_termination = CableTermination( + termination_type=ObjectType.objects.get_for_model(Interface), + termination_id=0, + ) + with self.assertRaises(ValueError): + cable_termination.cache_related_objects() + class VirtualDeviceContextTestCase(TestCase): diff --git a/netbox/dcim/ui/panels.py b/netbox/dcim/ui/panels.py index 478ddf634..bd2adc7f6 100644 --- a/netbox/dcim/ui/panels.py +++ b/netbox/dcim/ui/panels.py @@ -32,7 +32,7 @@ class RackDimensionsPanel(panels.ObjectAttributesPanel): outer_width = attrs.NumericAttr('outer_width', unit_accessor='get_outer_unit_display') outer_height = attrs.NumericAttr('outer_height', unit_accessor='get_outer_unit_display') outer_depth = attrs.NumericAttr('outer_depth', unit_accessor='get_outer_unit_display') - mounting_depth = attrs.TextAttr('mounting_depth', format_string=_('{} millimeters')) + mounting_depth = attrs.TextAttr('mounting_depth', format_string=_('{0} millimeters')) class RackNumberingPanel(panels.ObjectAttributesPanel): @@ -355,8 +355,8 @@ class PowerFeedElectricalPanel(panels.ObjectAttributesPanel): title = _('Electrical Characteristics') supply = attrs.ChoiceAttr('supply') - voltage = attrs.TextAttr('voltage', format_string=_('{}V')) - amperage = attrs.TextAttr('amperage', format_string=_('{}A')) + voltage = attrs.TextAttr('voltage', format_string='{}V') + amperage = attrs.TextAttr('amperage', format_string='{}A') phase = attrs.ChoiceAttr('phase') max_utilization = attrs.TextAttr('max_utilization', format_string='{}%') diff --git a/netbox/extras/events.py b/netbox/extras/events.py index 470adc875..c01e02ed5 100644 --- a/netbox/extras/events.py +++ b/netbox/extras/events.py @@ -125,7 +125,13 @@ def enqueue_event(queue, instance, request, event_type): app_label = instance._meta.app_label model_name = instance._meta.model_name - assert instance.pk is not None + if instance.pk is None: + raise ValueError( + _("Cannot enqueue an event for an unsaved {app_label}.{model} instance.").format( + app_label=app_label, + model=model_name, + ) + ) key = f'{app_label}.{model_name}:{instance.pk}' if key in queue: @@ -251,7 +257,7 @@ def process_event_rules(event_rules, object_type, event): if 'snapshots' in event: params['snapshots'] = event['snapshots'] if 'request' in event: - params['request'] = copy_safe_request(event['request']) + params['request'] = copy_safe_request(event['request'], include_files=False) # Enqueue the job ScriptJob.enqueue(**params) diff --git a/netbox/extras/fields.py b/netbox/extras/fields.py index 6cd44432f..735471757 100644 --- a/netbox/extras/fields.py +++ b/netbox/extras/fields.py @@ -1,4 +1,10 @@ -from django.db.models import TextField +from django.contrib.postgres.fields import ArrayField +from django.db.models import CharField, TextField + +__all__ = ( + 'CachedValueField', + 'ChoiceSetField', +) class CachedValueField(TextField): @@ -6,3 +12,18 @@ class CachedValueField(TextField): Currently a dummy field to prevent custom lookups being applied globally to TextField. """ pass + + +class ChoiceSetField(ArrayField): + """ + An ArrayField of two-element [value, label] string pairs representing custom field choices. + """ + def __init__(self, **kwargs): + kwargs['base_field'] = ArrayField(base_field=CharField(max_length=100), size=2) + super().__init__(**kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + # base_field is fixed by __init__ and omitted from migrations + del kwargs['base_field'] + return name, path, args, kwargs diff --git a/netbox/extras/forms/bulk_edit.py b/netbox/extras/forms/bulk_edit.py index 9fecfdb7a..abfe22f5a 100644 --- a/netbox/extras/forms/bulk_edit.py +++ b/netbox/extras/forms/bulk_edit.py @@ -212,7 +212,7 @@ class SavedFilterBulkEditForm(ChangelogMessageMixin, OwnerMixin, BulkEditForm): nullable_fields = ('description',) -class TableConfigBulkEditForm(BulkEditForm): +class TableConfigBulkEditForm(ChangelogMessageMixin, BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=TableConfig.objects.all(), widget=forms.MultipleHiddenInput diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 688f19563..0f8161632 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -403,7 +403,7 @@ class SavedFilterForm(ChangelogMessageMixin, OwnerMixin, forms.ModelForm): super().__init__(*args, initial=initial, **kwargs) -class TableConfigForm(forms.ModelForm): +class TableConfigForm(ChangelogMessageMixin, forms.ModelForm): object_type = ContentTypeChoiceField( label=_('Object type'), queryset=ObjectType.objects.all() @@ -439,10 +439,29 @@ class TableConfigForm(forms.ModelForm): def __init__(self, data=None, *args, **kwargs): super().__init__(data, *args, **kwargs) - object_type = ObjectType.objects.get(pk=get_field_value(self, 'object_type')) + self.fields['available_columns'].widget.choices = () + self.fields['columns'].widget.choices = () + + # Table context may be absent e.g. when the add view is requested directly + object_type_pk = get_field_value(self, 'object_type') + object_type_pk = getattr(object_type_pk, 'pk', object_type_pk) + if not object_type_pk: + return + + try: + object_type = ObjectType.objects.get(pk=object_type_pk) + except (ObjectType.DoesNotExist, TypeError, ValueError): + return + model = object_type.model_class() + if model is None: + return + table_name = get_field_value(self, 'table') table_class = get_table_for_model(model, table_name) + if table_class is None: + return + table = table_class([]) if columns := self._get_columns(): diff --git a/netbox/extras/forms/scripts.py b/netbox/extras/forms/scripts.py index b646c397d..2aa8f4786 100644 --- a/netbox/extras/forms/scripts.py +++ b/netbox/extras/forms/scripts.py @@ -112,5 +112,4 @@ class ScriptFileForm(ManagedFileForm): data = self.cleaned_data['upload_file'] storage.save(filename, data) - # need to skip ManagedFileForm save method - return super(ManagedFileForm, self).save(*args, **kwargs) + return super().save(*args, **kwargs) diff --git a/netbox/extras/graphql/filter_lookups.py b/netbox/extras/graphql/filter_lookups.py new file mode 100644 index 000000000..8394f8541 --- /dev/null +++ b/netbox/extras/graphql/filter_lookups.py @@ -0,0 +1,30 @@ +import strawberry +import strawberry_django +from django.db.models import Q, QuerySet +from strawberry.directive import DirectiveValue +from strawberry.types import Info + +__all__ = ( + 'ExtraChoicesLookup', +) + + +@strawberry.input( + one_of=True, + description='Lookup for extra choices defined on a choice set. Only one of the lookup fields can be set.', +) +class ExtraChoicesLookup: + contains: str | None = strawberry.field( + default=strawberry.UNSET, description='Has an extra choice with this value' + ) + length: int | None = strawberry.field( + default=strawberry.UNSET, description='Number of extra choices' + ) + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> tuple[QuerySet, Q]: + if self.contains is not strawberry.UNSET and self.contains is not None: + return queryset, Q(**{f'{prefix}choice_value': self.contains}) + if self.length is not strawberry.UNSET and self.length is not None: + return queryset, Q(**{f'{prefix}len': self.length}) + return queryset, Q() diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index afc1ff8f9..5b6e2eab6 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -1,4 +1,3 @@ -from datetime import datetime from typing import TYPE_CHECKING, Annotated import strawberry @@ -23,6 +22,7 @@ if TYPE_CHECKING: SiteFilter, SiteGroupFilter, ) + from extras.graphql.filter_lookups import ExtraChoicesLookup from netbox.graphql.enums import ColorEnum from netbox.graphql.filter_lookups import FloatLookup, IntegerLookup, JSONFilter, StringArrayLookup, TreeNodeFilter from tenancy.graphql.filters import TenantFilter, TenantGroupFilter @@ -54,11 +54,11 @@ __all__ = ( @strawberry_django.filter_type(models.ConfigContext, lookups=True) class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() is_active: FilterLookup[bool] | None = strawberry_django.filter_field() regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -111,22 +111,22 @@ class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): @strawberry_django.filter_type(models.ConfigContextProfile, lookups=True) class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.ConfigTemplate, lookups=True) class ConfigTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() - template_code: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() + template_code: StrFilterLookup | None = strawberry_django.filter_field() environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - mime_type: StrFilterLookup[str] | None = strawberry_django.filter_field() - file_name: StrFilterLookup[str] | None = strawberry_django.filter_field() - file_extension: StrFilterLookup[str] | None = strawberry_django.filter_field() + mime_type: StrFilterLookup | None = strawberry_django.filter_field() + file_name: StrFilterLookup | None = strawberry_django.filter_field() + file_extension: StrFilterLookup | None = strawberry_django.filter_field() as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() @@ -141,10 +141,10 @@ class CustomFieldFilter(ChangeLoggedModelFilter): related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - label: StrFilterLookup[str] | None = strawberry_django.filter_field() - group_name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + label: StrFilterLookup | None = strawberry_django.filter_field() + group_name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() required: FilterLookup[bool] | None = strawberry_django.filter_field() unique: FilterLookup[bool] | None = strawberry_django.filter_field() search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( @@ -170,7 +170,7 @@ class CustomFieldFilter(ChangeLoggedModelFilter): validation_maximum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - validation_regex: StrFilterLookup[str] | None = strawberry_django.filter_field() + validation_regex: StrFilterLookup | None = strawberry_django.filter_field() choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -186,19 +186,19 @@ class CustomFieldFilter(ChangeLoggedModelFilter): strawberry_django.filter_field() ) is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field() - comments: StrFilterLookup[str] | None = strawberry_django.filter_field() + comments: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.CustomFieldChoiceSet, lookups=True) class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() base_choices: ( BaseFilterLookup[Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')]] | None ) = ( strawberry_django.filter_field() ) - extra_choices: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + extra_choices: Annotated['ExtraChoicesLookup', strawberry.lazy('extras.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field() @@ -233,14 +233,14 @@ class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter): @strawberry_django.filter_type(models.CustomLink, lookups=True) class CustomLinkFilter(ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() enabled: FilterLookup[bool] | None = strawberry_django.filter_field() - link_text: StrFilterLookup[str] | None = strawberry_django.filter_field() - link_url: StrFilterLookup[str] | None = strawberry_django.filter_field() + link_text: StrFilterLookup | None = strawberry_django.filter_field() + link_url: StrFilterLookup | None = strawberry_django.filter_field() weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - group_name: StrFilterLookup[str] | None = strawberry_django.filter_field() + group_name: StrFilterLookup | None = strawberry_django.filter_field() button_class: ( BaseFilterLookup[Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')]] | None ) = ( @@ -251,15 +251,15 @@ class CustomLinkFilter(ChangeLoggedModelFilter): @strawberry_django.filter_type(models.ExportTemplate, lookups=True) class ExportTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() - template_code: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() + template_code: StrFilterLookup | None = strawberry_django.filter_field() environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - mime_type: StrFilterLookup[str] | None = strawberry_django.filter_field() - file_name: StrFilterLookup[str] | None = strawberry_django.filter_field() - file_extension: StrFilterLookup[str] | None = strawberry_django.filter_field() + mime_type: StrFilterLookup | None = strawberry_django.filter_field() + file_name: StrFilterLookup | None = strawberry_django.filter_field() + file_extension: StrFilterLookup | None = strawberry_django.filter_field() as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() @@ -275,7 +275,7 @@ class ImageAttachmentFilter(ChangeLoggedModelFilter): image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.JournalEntry, lookups=True) @@ -291,13 +291,13 @@ class JournalEntryFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedM kind: BaseFilterLookup[Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - comments: StrFilterLookup[str] | None = strawberry_django.filter_field() + comments: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.Notification, lookups=True) class NotificationFilter(BaseModelFilter): - created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() - read: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + created: DatetimeFilterLookup | None = strawberry_django.filter_field() + read: DatetimeFilterLookup | 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() object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( @@ -305,23 +305,23 @@ class NotificationFilter(BaseModelFilter): ) object_type_id: ID | None = strawberry_django.filter_field() object_id: ID | None = strawberry_django.filter_field() - object_repr: StrFilterLookup[str] | None = strawberry_django.filter_field() - event_type: StrFilterLookup[str] | None = strawberry_django.filter_field() + object_repr: StrFilterLookup | None = strawberry_django.filter_field() + event_type: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.NotificationGroup, lookups=True) class NotificationGroupFilter(ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.SavedFilter, lookups=True) class SavedFilterFilter(ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | 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() weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( @@ -336,7 +336,7 @@ class SavedFilterFilter(ChangeLoggedModelFilter): @strawberry_django.filter_type(models.Subscription, lookups=True) class SubscriptionFilter(BaseModelFilter): - created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + created: DatetimeFilterLookup | 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() object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( @@ -348,8 +348,8 @@ class SubscriptionFilter(BaseModelFilter): @strawberry_django.filter_type(models.TableConfig, lookups=True) class TableConfigFilter(ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | 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() weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( @@ -361,30 +361,30 @@ class TableConfigFilter(ChangeLoggedModelFilter): @strawberry_django.filter_type(models.Tag, lookups=True) class TagFilter(ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.Webhook, lookups=True) class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() - payload_url: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() + payload_url: StrFilterLookup | None = strawberry_django.filter_field() http_method: ( BaseFilterLookup[Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')]] | None ) = ( strawberry_django.filter_field() ) - http_content_type: StrFilterLookup[str] | None = strawberry_django.filter_field() - additional_headers: StrFilterLookup[str] | None = strawberry_django.filter_field() - body_template: StrFilterLookup[str] | None = strawberry_django.filter_field() - secret: StrFilterLookup[str] | None = strawberry_django.filter_field() + http_content_type: StrFilterLookup | None = strawberry_django.filter_field() + additional_headers: StrFilterLookup | None = strawberry_django.filter_field() + body_template: StrFilterLookup | None = strawberry_django.filter_field() + secret: StrFilterLookup | None = strawberry_django.filter_field() ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field() - ca_file_path: StrFilterLookup[str] | None = strawberry_django.filter_field() + ca_file_path: StrFilterLookup | None = strawberry_django.filter_field() events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -392,8 +392,8 @@ class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelF @strawberry_django.filter_type(models.EventRule, lookups=True) class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -412,4 +412,4 @@ class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedMode action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - comments: StrFilterLookup[str] | None = strawberry_django.filter_field() + comments: StrFilterLookup | None = strawberry_django.filter_field() diff --git a/netbox/extras/lookups.py b/netbox/extras/lookups.py index 062a2a01b..e1b4d2339 100644 --- a/netbox/extras/lookups.py +++ b/netbox/extras/lookups.py @@ -3,7 +3,16 @@ from django.contrib.postgres.fields.ranges import RangeField from django.db.models import CharField, JSONField, Lookup from django.db.models.fields.json import KeyTextTransform -from .fields import CachedValueField +from .fields import CachedValueField, ChoiceSetField + +__all__ = ( + 'ChoiceValueLookup', + 'Empty', + 'JSONEmpty', + 'NetContainsOrEquals', + 'NetHost', + 'RangeContains', +) class RangeContains(Lookup): @@ -34,6 +43,22 @@ class RangeContains(Lookup): return sql, params +class ChoiceValueLookup(Lookup): + """ + Match rows where any [value, label] pair in a ChoiceSetField has the given value. + + Compares the RHS against the first element (the value) of each pair. + """ + lookup_name = 'choice_value' + prepare_rhs = False + + def as_sql(self, compiler, connection): + lhs, lhs_params = self.process_lhs(compiler, connection) + rhs, rhs_params = self.process_rhs(compiler, connection) + # Slice the value column of the two-dimensional array and match any element + return f'{rhs} = ANY({lhs}[:][1:1])', [*rhs_params, *lhs_params] + + class Empty(Lookup): """ Filter on whether a string is empty. @@ -99,6 +124,7 @@ class NetContainsOrEquals(Lookup): ArrayField.register_lookup(RangeContains) +ChoiceSetField.register_lookup(ChoiceValueLookup) CharField.register_lookup(Empty) JSONField.register_lookup(JSONEmpty) CachedValueField.register_lookup(NetHost) diff --git a/netbox/extras/migrations/0139_alter_customfieldchoiceset_extra_choices.py b/netbox/extras/migrations/0139_alter_customfieldchoiceset_extra_choices.py new file mode 100644 index 000000000..b0f5704c6 --- /dev/null +++ b/netbox/extras/migrations/0139_alter_customfieldchoiceset_extra_choices.py @@ -0,0 +1,18 @@ +from django.db import migrations + +import extras.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0138_customfieldchoiceset_choice_colors'), + ] + + operations = [ + migrations.AlterField( + model_name='customfieldchoiceset', + name='extra_choices', + field=extras.fields.ChoiceSetField(blank=True, null=True), + ), + ] diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 011afa42d..31d417564 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -7,7 +7,6 @@ import django_filters import jsonschema from django import forms from django.conf import settings -from django.contrib.postgres.fields import ArrayField from django.core.validators import RegexValidator, ValidationError from django.db import models from django.db.models import F, Func, Value @@ -21,6 +20,7 @@ from jsonschema.exceptions import ValidationError as JSONValidationError from core.models import ObjectType from extras.choices import * from extras.data import CHOICE_SETS +from extras.fields import ChoiceSetField from netbox.context import query_cache from netbox.models import ChangeLoggedModel from netbox.models.features import CloningMixin, ExportTemplatesMixin @@ -461,6 +461,8 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo """ if value is None: return value + if self.type == CustomFieldTypeChoices.TYPE_DECIMAL: + return float(value) if self.type == CustomFieldTypeChoices.TYPE_DATE and type(value) is date: return value.isoformat() if self.type == CustomFieldTypeChoices.TYPE_DATETIME and type(value) is datetime: @@ -877,11 +879,7 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, Chang null=True, help_text=_('Base set of predefined choices (optional)') ) - extra_choices = ArrayField( - ArrayField( - base_field=models.CharField(max_length=100), - size=2 - ), + extra_choices = ChoiceSetField( blank=True, null=True ) diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 14cae8d9d..d538f94bf 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -649,6 +649,10 @@ class TableConfig(CloningMixin, ChangeLoggedModel): def clean(self): super().clean() + # Skip table validation until the object type and table have been set + if not self.object_type_id or not self.table: + return + # Validate table if self.table_class is None: raise ValidationError({ @@ -667,7 +671,7 @@ class TableConfig(CloningMixin, ChangeLoggedModel): }) # Validate selected columns - for name in self.columns: + for name in self.columns or []: if name not in table.columns: raise ValidationError({ 'columns': _('Unknown column: {name}').format(name=name) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 3a9703671..74eeb191c 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -325,6 +325,64 @@ class CustomFieldChoiceSetTestCase(APIViewTestCases.APIViewTestCase): response = self.client.post(self._get_list_url(), data, format='json', **self.header) self.assertEqual(response.status_code, 400) + def test_graphql_filter_extra_choices(self): + """Filter choice sets by choice value and by number of choices.""" + self.add_permissions('extras.view_customfieldchoiceset') + + # '1A' appears here only as a label, so it must not match contains + CustomFieldChoiceSet.objects.create( + name='Choice Set Labels', + extra_choices=[['sel1', 'Selection 1'], ['other', '1A']], + ) + + def run(lookup): + query = '{ custom_field_choice_set_list(filters: {extra_choices: ' + lookup + '}) { name } }' + response = self.client.post(reverse('graphql'), data={'query': query}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + data = response.json() + self.assertNotIn('errors', data) + return sorted(row['name'] for row in data['data']['custom_field_choice_set_list']) + + # contains matches choice values only, never labels + self.assertEqual(run('{contains: "1A"}'), ['Choice Set 1']) + self.assertEqual(run('{contains: "sel1"}'), ['Choice Set Labels']) + self.assertEqual(run('{contains: "Selection 1"}'), []) + # length is the number of [value, label] pairs + self.assertEqual(run('{length: 2}'), ['Choice Set Labels']) + self.assertEqual(run('{length: 1}'), []) + + def test_graphql_filter_extra_choices_rejects_array_operands(self): + """The legacy flat and nested array operand shapes fail schema validation.""" + self.add_permissions('extras.view_customfieldchoiceset') + + def run_invalid(lookup): + query = '{ custom_field_choice_set_list(filters: {extra_choices: ' + lookup + '}) { name } }' + response = self.client.post(reverse('graphql'), data={'query': query}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertIn('errors', response.json()) + + # shapes advertised or attempted before #22324 + run_invalid('{contains: ["1A"]}') + run_invalid('{contains: [["1A", "Choice 1A"]]}') + + def test_graphql_filter_extra_choices_via_relation(self): + """The extra_choices lookup composes through the choice_set relation prefix.""" + self.add_permissions('extras.view_customfield') + + for choice_set in CustomFieldChoiceSet.objects.filter(name__in=['Choice Set 1', 'Choice Set 2']): + CustomField.objects.create( + name=f'cf_{choice_set.name[-1]}', + type=CustomFieldTypeChoices.TYPE_SELECT, + choice_set=choice_set, + ) + + query = '{ custom_field_list(filters: {choice_set: {extra_choices: {contains: "1A"}}}) { name } }' + response = self.client.post(reverse('graphql'), data={'query': query}, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + data = response.json() + self.assertNotIn('errors', data) + self.assertEqual([row['name'] for row in data['data']['custom_field_list']], ['cf_1']) + class CustomLinkTestCase(APIViewTestCases.APIViewTestCase): model = CustomLink diff --git a/netbox/extras/tests/test_event_rules.py b/netbox/extras/tests/test_event_rules.py index ddd1b05b8..cc0f0bd4d 100644 --- a/netbox/extras/tests/test_event_rules.py +++ b/netbox/extras/tests/test_event_rules.py @@ -1,30 +1,36 @@ import json +import logging import uuid +from io import BytesIO from unittest import skipIf from unittest.mock import Mock, patch import django_rq from django.conf import settings from django.http import HttpResponse -from django.test import RequestFactory +from django.test import RequestFactory, tag from django.urls import reverse +from PIL import Image from requests import Session from rest_framework import status +from core.choices import ManagedFileRootPathChoices from core.events import * -from core.models import ObjectType +from core.models import Job, ObjectType from dcim.choices import SiteStatusChoices -from dcim.models import Site +from dcim.models import DeviceType, Interface, Manufacturer, Site from extras.choices import EventRuleActionChoices from extras.events import enqueue_event, flush_events, serialize_for_event -from extras.models import EventRule, Script, Tag, Webhook +from extras.models import EventRule, Script, ScriptModule, Tag, Webhook +from extras.scripts import Script as ScriptBase from extras.signals import process_job_end_event_rules from extras.webhooks import generate_signature, send_webhook from netbox.context_managers import event_tracking -from utilities.testing import APITestCase +from utilities.testing import APITestCase, create_test_device +from utilities.testing.mixins import RQQueueTestMixin -class EventRuleTestCase(APITestCase): +class EventRuleTestCase(RQQueueTestMixin, APITestCase): def setUp(self): super().setUp() @@ -39,6 +45,16 @@ class EventRuleTestCase(APITestCase): # Clear the queue so leftover jobs do not leak to the next test suite self.queue.empty() + def test_enqueue_event_requires_saved_instance(self): + """enqueue_event raises ValueError for an unsaved instance.""" + request = RequestFactory().get('/') + request.id = uuid.uuid4() + request.user = self.user + site = Site(name='Site 1', slug='site-1') + with patch('extras.events.has_feature', return_value=True): + with self.assertRaises(ValueError): + enqueue_event({}, site, request, OBJECT_CREATED) + @classmethod def setUpTestData(cls): @@ -531,6 +547,48 @@ class EventRuleTestCase(APITestCase): self.assertEqual(event['data']['name'], 'Site 1') self.assertIsNone(event['snapshots']['postchange']) + @tag('regression') # #21338 + def test_cable_creation_event_payload_includes_connected_endpoints(self): + """ + Interface update events queued during cable creation must include the + peer interface in connected_endpoints and link_peers. + """ + webhook = Webhook.objects.get(name='Webhook 1') + event_rule = EventRule.objects.create( + name='Interface Update Rule', + event_types=[OBJECT_UPDATED], + action_type=EventRuleActionChoices.WEBHOOK, + action_object_type=ObjectType.objects.get_for_model(Webhook), + action_object_id=webhook.id, + ) + event_rule.object_types.set([ObjectType.objects.get_for_model(Interface)]) + + device = create_test_device('Device 1') + interface_a = Interface.objects.create(device=device, name='eth0') + interface_b = Interface.objects.create(device=device, name='eth1') + + # Create a cable between the two interfaces via the REST API + data = { + 'a_terminations': [{'object_type': 'dcim.interface', 'object_id': interface_a.pk}], + 'b_terminations': [{'object_type': 'dcim.interface', 'object_id': interface_b.pk}], + } + url = reverse('dcim-api:cable-list') + self.add_permissions('dcim.add_cable') + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + + # One update event was queued for each interface + self.assertEqual(self.queue.count, 2) + payloads = {job.kwargs['data']['id']: job.kwargs['data'] for job in self.queue.jobs} + peers = {interface_a.pk: interface_b.pk, interface_b.pk: interface_a.pk} + self.assertEqual(set(payloads), set(peers)) + for interface_id, payload in payloads.items(): + peer_id = peers[interface_id] + self.assertIsNotNone(payload['connected_endpoints']) + self.assertEqual([endpoint['id'] for endpoint in payload['connected_endpoints']], [peer_id]) + self.assertEqual([peer['id'] for peer in payload['link_peers']], [peer_id]) + self.assertTrue(payload['connected_endpoints_reachable']) + def test_duplicate_triggers(self): """ Test for erroneous duplicate event triggers resulting from saving an object multiple times @@ -601,3 +659,145 @@ class EventRuleTestCase(APITestCase): self.add_permissions('dcim.add_site') response = self.client.post(url, {'name': 'Site X', 'slug': 'site-x'}, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_201_CREATED) + + @tag('regression') + def test_eventrule_script_action_with_object_image_files(self): + """ + Verify that a Script event-rule action can be enqueued and executed cleanly when the + triggering object carries uploaded files (e.g. DeviceType images). + This is a regression test for issue #22376. + + """ + # Create a dummy script class and an instance of it + class DummyScript(ScriptBase): + class Meta: + name = "Dummy Script" + + def run(self, data, commit=True): + return "finished successfully" + + dummy_script = DummyScript() + + # Create ScriptModule and Script + with patch.object(ScriptModule, 'sync_classes'): + module = ScriptModule.objects.create( + file_root=ManagedFileRootPathChoices.SCRIPTS, + file_path='dummy_script.py', + ) + script = Script.objects.create( + module=module, + name='Dummy Script', + is_executable=True, + ) + script_type = ObjectType.objects.get_for_model(Script) + + # Create an event rule that triggers on DeviceType update with Script action + devicetype_type = ObjectType.objects.get_for_model(DeviceType) + event_rule = EventRule.objects.create( + name='Test Script Event Rule with Files', + event_types=[OBJECT_UPDATED], + action_type=EventRuleActionChoices.SCRIPT, + action_object_type=script_type, + action_object_id=script.pk, + ) + event_rule.object_types.set([devicetype_type]) + + # Create a manufacturer and DeviceType + manufacturer = Manufacturer.objects.create( + name='Test Manufacturer', + slug='test-manufacturer', + ) + devicetype = DeviceType.objects.create( + model='Test DeviceType', + slug="test-devicetype", + manufacturer=manufacturer, + ) + + # Create an image file + image = BytesIO() + Image.new('RGB', (1, 1)).save(image, format='PNG') + image.name = 'test_image.png' + image.seek(0) + + # PATCH the DeviceType via REST API to add the image + data = { + 'front_image': image, + } + url = reverse('dcim-api:devicetype-detail', kwargs={'pk': devicetype.pk}) + self.add_permissions('dcim.change_devicetype') + + # Mock the script's python_class to prevent the test from trying to load from disk + with patch.object(Script, 'python_class') as mock: + mock.return_value = dummy_script + # Since in core/models/jobs.py Jobs are enqueued with a transaction.on_commit-handler + # we simulate commit by using captureOnCommitCallbacks context manager + with self.captureOnCommitCallbacks(execute=True): + response = self.client.patch(url, data, format='multipart', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Assert that the script job was enqueued cleanly and is waiting for execution + self.assertEqual(self.queue.count, 1) + script_job = Job.objects.filter(name=dummy_script.name).last() + self.assertEqual(script_job.status, "pending") + + # silence rqworker (cleaner output) and trigger job execution + logging.getLogger('rq.worker').setLevel(logging.ERROR) + self.run_rq_jobs('default') + + # Assert that our script was executed without any errors + script_job.refresh_from_db() + self.assertEqual(script_job.status, "completed") + self.assertEqual(script_job.data.get('output', ''), "finished successfully") + + @tag('regression') + def test_eventrule_webhook_action_with_object_image_files(self): + """ + Verify that a Webhook event-rule action can be enqueued and executed cleanly when + the triggering object carries uploaded files (e.g. DeviceType images). + This is a regression test for issue #20873. + """ + # Create an event rule that triggers on DeviceType update with Script action + webhook = Webhook.objects.get(name='Webhook 1') + webhook_type = ObjectType.objects.get_for_model(Webhook) + devicetype_type = ObjectType.objects.get_for_model(DeviceType) + event_rule = EventRule.objects.create( + name='Test Webhook Event Rule with Files', + event_types=[OBJECT_UPDATED], + action_type=EventRuleActionChoices.WEBHOOK, + action_object_type=webhook_type, + action_object_id=webhook.pk, + ) + event_rule.object_types.set([devicetype_type]) + + # Create a manufacturer and DeviceType + manufacturer = Manufacturer.objects.create( + name='Test Manufacturer', + slug='test-manufacturer', + ) + devicetype = DeviceType.objects.create( + model='Test DeviceType', + slug="test-devicetype", + manufacturer=manufacturer, + ) + + # Create an image file + image = BytesIO() + Image.new('RGB', (1, 1)).save(image, format='PNG') + image.name = 'test_image.png' + image.seek(0) + + # PATCH the DeviceType via REST API to add the image + data = { + 'front_image': image, + } + url = reverse('dcim-api:devicetype-detail', kwargs={'pk': devicetype.pk}) + self.add_permissions('dcim.change_devicetype') + + response = self.client.patch(url, data, format='multipart', **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + # Assert that the webhook job was enqueued cleanly + self.assertEqual(self.queue.count, 1) + job = self.queue.jobs[0] + self.assertEqual(job.kwargs['event_rule'], event_rule) + self.assertEqual(job.kwargs['event_type'], OBJECT_UPDATED) diff --git a/netbox/extras/tests/test_forms.py b/netbox/extras/tests/test_forms.py index 26de1de80..909d72801 100644 --- a/netbox/extras/tests/test_forms.py +++ b/netbox/extras/tests/test_forms.py @@ -10,7 +10,7 @@ from core.models import DataSource, ObjectType from dcim.forms import SiteForm from dcim.models import Site from extras.choices import CustomFieldTypeChoices -from extras.forms import SavedFilterForm +from extras.forms import SavedFilterForm, TableConfigBulkEditForm, TableConfigForm from extras.forms.model_forms import CustomFieldChoiceSetForm from extras.forms.scripts import ScriptFileForm from extras.models import CustomField, CustomFieldChoiceSet, ScriptModule @@ -288,3 +288,52 @@ class ScriptFileFormTestCase(TestCase): form = ScriptFileForm(files={'upload_file': upload_file}, instance=self._new_module()) self.assertTrue(form.is_valid()) + + +class TableConfigFormTestCase(TestCase): + + def test_form_without_table_context(self): + """The form must be constructible without an object type.""" + form = TableConfigForm() + self.assertEqual(list(form.fields['available_columns'].widget.choices), []) + self.assertEqual(list(form.fields['columns'].widget.choices), []) + + def test_form_with_invalid_object_type(self): + """An unknown object type must yield empty column choices.""" + last_pk = ObjectType.objects.order_by('pk').last().pk + form = TableConfigForm(initial={'object_type': last_pk + 1}) + self.assertEqual(list(form.fields['available_columns'].widget.choices), []) + self.assertEqual(list(form.fields['columns'].widget.choices), []) + + def test_form_with_unknown_table(self): + """An unresolvable table name must yield empty column choices.""" + object_type = ObjectType.objects.get_for_model(Site) + form = TableConfigForm(initial={'object_type': object_type.pk, 'table': 'NoSuchTable'}) + self.assertEqual(list(form.fields['columns'].widget.choices), []) + + def test_form_with_table_context(self): + """Column choices must be populated from the resolved table.""" + object_type = ObjectType.objects.get_for_model(Site) + form = TableConfigForm(initial={ + 'object_type': object_type.pk, + 'table': 'SiteTable', + 'columns': ['name', 'status'], + }) + self.assertEqual( + [name for name, _ in form.fields['columns'].widget.choices], + ['name', 'status'] + ) + self.assertIn('region', dict(form.fields['available_columns'].widget.choices)) + + def test_form_includes_changelog_message(self): + """The model form must expose the changelog_message meta field.""" + object_type = ObjectType.objects.get_for_model(Site) + form = TableConfigForm(initial={'object_type': object_type.pk, 'table': 'SiteTable'}) + self.assertIn('changelog_message', form.fields) + self.assertIn('changelog_message', form.meta_fields) + + def test_bulk_edit_form_includes_changelog_message(self): + """The bulk edit form must expose the changelog_message meta field.""" + form = TableConfigBulkEditForm() + self.assertIn('changelog_message', form.fields) + self.assertIn('changelog_message', form.meta_fields) diff --git a/netbox/extras/tests/test_lookups.py b/netbox/extras/tests/test_lookups.py new file mode 100644 index 000000000..7188c2ed6 --- /dev/null +++ b/netbox/extras/tests/test_lookups.py @@ -0,0 +1,31 @@ +from django.core.exceptions import FieldError +from django.test import TestCase + +from extras.choices import CustomFieldChoiceSetBaseChoices +from extras.models import CustomFieldChoiceSet, EventRule + + +class ChoiceValueLookupTestCase(TestCase): + + def test_choice_value_matches_values_only(self): + """choice_value matches the value element of a pair, never the label.""" + CustomFieldChoiceSet.objects.create( + name='Choice Set 1', + extra_choices=[['sel1', 'Selection 1'], ['other', 'sel2']], + ) + self.assertEqual(CustomFieldChoiceSet.objects.filter(extra_choices__choice_value='sel1').count(), 1) + self.assertEqual(CustomFieldChoiceSet.objects.filter(extra_choices__choice_value='sel2').count(), 0) + + def test_choice_value_excludes_null_extra_choices(self): + """Choice sets without extra choices are excluded without raising.""" + CustomFieldChoiceSet.objects.create( + name='Base Only', + base_choices=CustomFieldChoiceSetBaseChoices.IATA, + ) + self.assertEqual(CustomFieldChoiceSet.objects.filter(extra_choices__choice_value='sel1').count(), 0) + self.assertEqual(CustomFieldChoiceSet.objects.filter(extra_choices__len=2).count(), 0) + + def test_choice_value_not_registered_on_plain_array_fields(self): + """choice_value is scoped to ChoiceSetField and unavailable on other ArrayFields.""" + with self.assertRaises(FieldError): + EventRule.objects.filter(event_types__choice_value='x').exists() diff --git a/netbox/extras/tests/test_models.py b/netbox/extras/tests/test_models.py index 7f7721258..fbbe93267 100644 --- a/netbox/extras/tests/test_models.py +++ b/netbox/extras/tests/test_models.py @@ -210,6 +210,26 @@ class TableConfigTestCase(TestCase): # Must not raise TypeError: 'NoneType' object is not iterable tc.full_clean() + def test_clean_without_object_type(self): + """full_clean() on an instance missing its object type must raise ValidationError.""" + tc = TableConfig( + table=self.table_name, + name='No object type', + columns=['name'], + ) + with self.assertRaises(ValidationError): + tc.full_clean() + + def test_clean_accepts_columns_none(self): + """full_clean() must report missing columns rather than raise TypeError.""" + tc = TableConfig( + object_type=self.site_ct, + table=self.table_name, + name='No columns', + ) + with self.assertRaises(ValidationError): + tc.full_clean() + class TagTestCase(TestCase): diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py index 74c88cea8..8dd82c239 100644 --- a/netbox/extras/tests/test_views.py +++ b/netbox/extras/tests/test_views.py @@ -2,6 +2,7 @@ import uuid from unittest.mock import PropertyMock, patch from django.contrib.contenttypes.models import ContentType +from django.contrib.messages import get_messages from django.test import tag from django.urls import reverse @@ -277,13 +278,16 @@ class SavedFilterTestCase(ViewTestCases.PrimaryObjectViewTestCase): class TableConfigTestCase( ViewTestCases.GetObjectViewTestCase, ViewTestCases.GetObjectChangelogViewTestCase, - ViewTestCases.ListObjectsViewTestCase, + ViewTestCases.CreateObjectViewTestCase, + ViewTestCases.EditObjectViewTestCase, ViewTestCases.DeleteObjectViewTestCase, + ViewTestCases.ListObjectsViewTestCase, + ViewTestCases.BulkEditObjectsViewTestCase, ViewTestCases.BulkDeleteObjectsViewTestCase, ): - # Add/Edit/BulkEdit views require an object_type pre-context from the source - # table view, so they are not exercised here. model = TableConfig + # Selected columns are POSTed as a list but compared as a CSV string + validation_excluded_fields = ('columns',) @classmethod def setUpTestData(cls): @@ -320,6 +324,45 @@ class TableConfigTestCase( ) TableConfig.objects.bulk_create(table_configs) + cls.form_data = { + 'name': 'Table Config X', + 'object_type': site_type.pk, + 'table': 'SiteTable', + 'description': 'A table config', + 'weight': 100, + 'enabled': True, + 'shared': True, + 'columns': ['name', 'status'], + 'ordering': 'name', + } + cls.bulk_edit_data = { + 'weight': 999, + } + + def _get_url(self, action, instance=None): + url = super()._get_url(action, instance) + # The add view requires the table context from the source table view + if action == 'add': + site_type = ObjectType.objects.get_for_model(Site) + url = f'{url}?object_type={site_type.pk}&table=SiteTable' + return url + + def test_add_view_without_table_context(self): + """A GET without the table context params must redirect to the home page.""" + self.add_permissions('extras.add_tableconfig') + response = self.client.get(reverse('extras:tableconfig_add')) + self.assertRedirects(response, reverse('home')) + + messages_list = list(get_messages(response.wsgi_request)) + self.assertEqual(len(messages_list), 1) + self.assertEqual(str(messages_list[0]), 'Table configurations must be created from an object list view.') + + def test_add_view_post_without_table_context(self): + """A POST without the table context must return form errors rather than a server error.""" + self.add_permissions('extras.add_tableconfig') + response = self.client.post(reverse('extras:tableconfig_add'), data={}) + self.assertHttpStatus(response, 200) + class BookmarkTestCase( ViewTestCases.DeleteObjectViewTestCase, @@ -365,6 +408,9 @@ class BookmarkTestCase( def test_list_objects_anonymous(self): return + def test_export_objects_anonymous(self): + return + def test_list_objects_with_constrained_permission(self): return @@ -919,6 +965,9 @@ class SubscriptionTestCase( login_url = reverse('login') self.assertRedirects(self.client.get(url), f'{login_url}?next={url}') + def test_export_objects_anonymous(self): + return + def test_list_objects_with_permission(self): return @@ -1027,6 +1076,9 @@ class NotificationTestCase( login_url = reverse('login') self.assertRedirects(self.client.get(url), f'{login_url}?next={url}') + def test_export_objects_anonymous(self): + return + def test_list_objects_with_permission(self): return diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 344830e67..cec119378 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -481,6 +481,14 @@ class TableConfigEditView(SharedObjectViewMixin, generic.ObjectEditView): form = forms.TableConfigForm template_name = 'extras/tableconfig_edit.html' + def get(self, request, *args, **kwargs): + # The add view requires the object_type & table parameters from the source table view + if not kwargs and not (request.GET.get('object_type') and request.GET.get('table')): + messages.warning(request, _('Table configurations must be created from an object list view.')) + return redirect('home') + + return super().get(request, *args, **kwargs) + def alter_object(self, obj, request, url_args, url_kwargs): if not obj.pk: obj.user = request.user diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 14f39903f..cdc6786f0 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -407,7 +407,7 @@ class AvailableIPAddressesView(AvailableObjectsView): def get_available_objects(self, parent, limit=None): # Calculate available IPs within the parent ip_list = [] - for index, ip in enumerate(parent.get_available_ips(), start=1): + for index, ip in enumerate(parent.iter_available_ips(), start=1): ip_list.append(ip) if index == limit: break diff --git a/netbox/ipam/fields.py b/netbox/ipam/fields.py index b83c4ca30..11fa5bac0 100644 --- a/netbox/ipam/fields.py +++ b/netbox/ipam/fields.py @@ -42,7 +42,10 @@ class BaseIPField(models.Field): raise ValidationError(e) def get_prep_value(self, value): - if not value: + # Membership check; `not value` incorrectly treats the valid zero addresses + # 0.0.0.0 and :: as empty. netaddr objects compare unequal to all three + # sentinels; raw int 0 stays "empty" for backward compatibility. + if value in (None, '', 0): return None if isinstance(value, list): return [str(self.to_python(v)) for v in value] @@ -107,6 +110,7 @@ IPAddressField.register_lookup(lookups.NetContainsOrEquals) IPAddressField.register_lookup(lookups.NetHost) IPAddressField.register_lookup(lookups.NetIn) IPAddressField.register_lookup(lookups.NetHostContained) +IPAddressField.register_lookup(lookups.NetHostBetween) IPAddressField.register_lookup(lookups.NetFamily) IPAddressField.register_lookup(lookups.NetMaskLength) IPAddressField.register_lookup(lookups.Host) diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index bba050215..c95e76d14 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -1142,19 +1142,17 @@ class VLANFilterSet(PrimaryModelFilterSet, TenancyFilterSet): def filter_interface_id(self, queryset, name, value): if value is None: return queryset.none() - return queryset.filter( - Q(interfaces_as_tagged=value) | - Q(interfaces_as_untagged=value) - ).distinct() + tagged = queryset.filter(interfaces_as_tagged=value) + untagged = queryset.filter(interfaces_as_untagged=value) + return queryset.filter(pk__in=tagged.union(untagged).values('pk')) @extend_schema_field(OpenApiTypes.INT) def filter_vminterface_id(self, queryset, name, value): if value is None: return queryset.none() - return queryset.filter( - Q(vminterfaces_as_tagged=value) | - Q(vminterfaces_as_untagged=value) - ).distinct() + tagged = queryset.filter(vminterfaces_as_tagged=value) + untagged = queryset.filter(vminterfaces_as_untagged=value) + return queryset.filter(pk__in=tagged.union(untagged).values('pk')) @register_filterset diff --git a/netbox/ipam/forms/bulk_create.py b/netbox/ipam/forms/bulk_create.py index 763dbac9c..da6bd0fdb 100644 --- a/netbox/ipam/forms/bulk_create.py +++ b/netbox/ipam/forms/bulk_create.py @@ -1,10 +1,12 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from utilities.forms.fields import ExpandableIPNetworkField +from ipam.constants import VLAN_VID_MAX, VLAN_VID_MIN +from utilities.forms.fields import ExpandableIPNetworkField, NumericArrayField __all__ = ( 'IPNetworkBulkCreateForm', + 'VLANIDBulkCreateForm', ) @@ -15,3 +17,17 @@ class IPNetworkBulkCreateForm(forms.Form): pattern = ExpandableIPNetworkField( label=_('Pattern') ) + + +class VLANIDBulkCreateForm(forms.Form): + pattern = NumericArrayField( + base_field=forms.IntegerField( + min_value=VLAN_VID_MIN, + max_value=VLAN_VID_MAX + ), + label=_('VLAN IDs'), + help_text=_( + 'Enter VLAN IDs and ranges separated by commas. ' + 'Example: 100,200-210,3100-3299' + ) + ) diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index ac629c05c..4dce7baff 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -45,6 +45,7 @@ __all__ = ( 'ServiceCreateForm', 'ServiceForm', 'ServiceTemplateForm', + 'VLANBulkAddForm', 'VLANForm', 'VLANGroupForm', 'VLANTranslationPolicyForm', @@ -727,6 +728,26 @@ class VLANForm(TenancyForm, PrimaryModelForm): ] +class VLANBulkAddForm(VLANForm): + """ + Subclass of VLANForm for bulk creation. + + The VID field is inherited but excluded from the visible fieldsets, as it is + populated programmatically by BulkCreateView from the expanded pattern. + """ + fieldsets = ( + FieldSet('group', 'site', 'name', 'status', 'role', 'description', 'tags', name=_('VLAN')), + FieldSet('qinq_role', 'qinq_svlan', name=_('Q-in-Q/802.1ad')), + FieldSet('tenant_group', 'tenant', name=_('Tenancy')), + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['name'].help_text = _( + 'Use {vid} as a placeholder for the VLAN ID. Example: VLAN-{vid}.' + ) + + class VLANTranslationPolicyForm(PrimaryModelForm): fieldsets = ( diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index f3c6637fa..7d16c0fe6 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -1,4 +1,3 @@ -from datetime import date from typing import TYPE_CHECKING, Annotated import netaddr @@ -72,8 +71,8 @@ class ASNFilter(TenancyFilterMixin, PrimaryModelFilter): @strawberry_django.filter_type(models.ASNRange, lookups=True) class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() start: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( @@ -86,10 +85,10 @@ class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter): @strawberry_django.filter_type(models.Aggregate, lookups=True) class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter): - prefix: StrFilterLookup[str] | None = strawberry_django.filter_field() + prefix: StrFilterLookup | None = strawberry_django.filter_field() rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() - date_added: DateFilterLookup[date] | None = strawberry_django.filter_field() + date_added: DateFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_field() def contains(self, value: list[str], prefix) -> Q: @@ -122,14 +121,14 @@ class FHRPGroupFilter(PrimaryModelFilter): group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() protocol: BaseFilterLookup[Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')]] | None = ( strawberry_django.filter_field() ) auth_type: BaseFilterLookup[Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')]] | None = ( strawberry_django.filter_field() ) - auth_key: StrFilterLookup[str] | None = strawberry_django.filter_field() + auth_key: StrFilterLookup | None = strawberry_django.filter_field() ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -140,7 +139,7 @@ class FHRPGroupAssignmentFilter(ChangeLoggedModelFilter): interface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) - interface_id: StrFilterLookup[str] | None = strawberry_django.filter_field() + interface_id: StrFilterLookup | None = strawberry_django.filter_field() group: Annotated['FHRPGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -176,7 +175,7 @@ class FHRPGroupAssignmentFilter(ChangeLoggedModelFilter): @strawberry_django.filter_type(models.IPAddress, lookups=True) class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter): - address: StrFilterLookup[str] | None = strawberry_django.filter_field() + address: StrFilterLookup | None = strawberry_django.filter_field() vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() vrf_id: ID | None = strawberry_django.filter_field() status: BaseFilterLookup[Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = ( @@ -197,7 +196,7 @@ class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter strawberry_django.filter_field() ) nat_outside_id: ID | None = strawberry_django.filter_field() - dns_name: StrFilterLookup[str] | None = strawberry_django.filter_field() + dns_name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_field() def assigned(self, value: bool, prefix) -> Q: @@ -227,8 +226,8 @@ class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter @strawberry_django.filter_type(models.IPRange, lookups=True) class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter): - start_address: StrFilterLookup[str] | None = strawberry_django.filter_field() - end_address: StrFilterLookup[str] | None = strawberry_django.filter_field() + start_address: StrFilterLookup | None = strawberry_django.filter_field() + end_address: StrFilterLookup | None = strawberry_django.filter_field() size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -281,7 +280,7 @@ class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter): @strawberry_django.filter_type(models.Prefix, lookups=True) class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter): - prefix: StrFilterLookup[str] | None = strawberry_django.filter_field() + prefix: StrFilterLookup | None = strawberry_django.filter_field() vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() vrf_id: ID | None = strawberry_django.filter_field() vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() @@ -330,7 +329,7 @@ class RoleFilter(OrganizationalModelFilter): @strawberry_django.filter_type(models.RouteTarget, lookups=True) class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() importing_vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -347,7 +346,7 @@ class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilter): @strawberry_django.filter_type(models.Service, lookups=True) class ServiceFilter(ContactFilterMixin, ServiceFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -359,7 +358,7 @@ class ServiceFilter(ContactFilterMixin, ServiceFilterMixin, PrimaryModelFilter): @strawberry_django.filter_type(models.ServiceTemplate, lookups=True) class ServiceTemplateFilter(ServiceFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.VLAN, lookups=True) @@ -373,7 +372,7 @@ class VLANFilter(TenancyFilterMixin, PrimaryModelFilter): vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() status: BaseFilterLookup[Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = ( strawberry_django.filter_field() ) @@ -404,7 +403,7 @@ class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilter): @strawberry_django.filter_type(models.VLANTranslationPolicy, lookups=True) class VLANTranslationPolicyFilter(PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() @strawberry_django.filter_type(models.VLANTranslationRule, lookups=True) @@ -413,7 +412,7 @@ class VLANTranslationRuleFilter(NetBoxModelFilter): strawberry_django.filter_field() ) policy_id: ID | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() local_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -424,8 +423,8 @@ class VLANTranslationRuleFilter(NetBoxModelFilter): @strawberry_django.filter_type(models.VRF, lookups=True) class VRFFilter(TenancyFilterMixin, PrimaryModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - rd: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + rd: StrFilterLookup | None = strawberry_django.filter_field() enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field() import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index 31b93b419..f17c9c72e 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -244,7 +244,7 @@ class RouteTargetType(PrimaryObjectType): @strawberry_django.type( models.Service, - exclude=('parent_object_type', 'parent_object_id'), + exclude=('_ports_lowest', 'parent_object_type', 'parent_object_id'), filters=ServiceFilter, pagination=True ) @@ -264,7 +264,7 @@ class ServiceType(ContactsMixin, PrimaryObjectType): @strawberry_django.type( models.ServiceTemplate, - fields='__all__', + exclude=('_ports_lowest',), filters=ServiceTemplateFilter, pagination=True ) diff --git a/netbox/ipam/lookups.py b/netbox/ipam/lookups.py index 693903f3f..309917699 100644 --- a/netbox/ipam/lookups.py +++ b/netbox/ipam/lookups.py @@ -1,3 +1,4 @@ +import netaddr from django.db.models import IntegerField, Lookup, Transform, lookups @@ -99,7 +100,8 @@ class NetHost(Lookup): if rhs_params: rhs_params[0] = rhs_params[0].split('/')[0] params = list(lhs_params) + rhs_params - return f'HOST({lhs}) = {rhs}', params + # Cast to INET so the predicate matches the inet ipam_ipaddress_host index. + return f'CAST(HOST({lhs}) AS INET) = {rhs}', params class NetIn(Lookup): @@ -120,7 +122,8 @@ class NetIn(Lookup): without_mask.append(address) address_in_clause = self.create_in_clause('{} IN ('.format(lhs), len(with_mask)) - host_in_clause = self.create_in_clause('HOST({}) IN ('.format(lhs), len(without_mask)) + # Cast to INET so the predicate matches the inet ipam_ipaddress_host index. + host_in_clause = self.create_in_clause('CAST(HOST({}) AS INET) IN ('.format(lhs), len(without_mask)) if with_mask and not without_mask: return address_in_clause, with_mask @@ -156,6 +159,34 @@ class NetHostContained(Lookup): return f'CAST(HOST({lhs}) AS INET) <<= {rhs}', params +class NetHostBetween(Lookup): + """ + Match host addresses (mask ignored) falling inclusively between two bounds. The left-hand + side is kept as an inet-typed host expression so PostgreSQL can use the host expression + indexes on the IPAM address and range tables; the CAST(HOST(...) AS INET) spelling matches + NetHost/NetIn for consistency (PostgreSQL canonicalizes the INET(HOST(...)) function form + to the same expression). + """ + lookup_name = 'host_between' + + def get_prep_lookup(self): + if not isinstance(self.rhs, (list, tuple)) or len(self.rhs) != 2: + raise ValueError('The host_between lookup requires a (lower, upper) pair of bounds') + try: + # Normalize to bare hosts; reject malformed values before they reach SQL. + lower, upper = (netaddr.IPNetwork(str(bound)).ip for bound in self.rhs) + except (netaddr.AddrFormatError, ValueError) as e: + raise ValueError(f'Invalid host_between bound: {e}') from e + if lower.version != upper.version: + raise ValueError('host_between bounds must not mix address families') + return lower, upper + + def as_sql(self, qn, connection): + lhs, lhs_params = self.process_lhs(qn, connection) + params = list(lhs_params) + [str(bound) for bound in self.rhs] + return f'CAST(HOST({lhs}) AS INET) BETWEEN %s AND %s', params + + class NetFamily(Transform): lookup_name = 'family' function = 'FAMILY' diff --git a/netbox/ipam/managers.py b/netbox/ipam/managers.py index 1ef00e125..8f1eb9ff1 100644 --- a/netbox/ipam/managers.py +++ b/netbox/ipam/managers.py @@ -1,10 +1,10 @@ from django.db.models import Manager from ipam.lookups import Host, Inet -from utilities.querysets import RestrictedQuerySet +from ipam.querysets import IPAddressQuerySet -class IPAddressManager(Manager.from_queryset(RestrictedQuerySet)): +class IPAddressManager(Manager.from_queryset(IPAddressQuerySet)): def get_queryset(self): """ diff --git a/netbox/ipam/migrations/0089_default_ordering_indexes.py b/netbox/ipam/migrations/0089_default_ordering_indexes.py index a718aa3fc..64ec1b020 100644 --- a/netbox/ipam/migrations/0089_default_ordering_indexes.py +++ b/netbox/ipam/migrations/0089_default_ordering_indexes.py @@ -32,9 +32,12 @@ class Migration(migrations.Migration): model_name='role', index=models.Index(fields=['weight', 'name'], name='ipam_role_weight_01396b_idx'), ), + # Adding a dummy index, to allow a safe migration in case updating users already have services + # with a large number of ports configured (see issue #22273) + # Will get removed in 0091_alter_service_index_and_ordering migrations.AddIndex( model_name='service', - index=models.Index(fields=['protocol', 'ports', 'id'], name='ipam_servic_protoco_687d13_idx'), + index=models.Index(fields=['id'], name='ipam_servic_protoco_687d13_idx'), ), migrations.AddIndex( model_name='vlangroup', diff --git a/netbox/ipam/migrations/0091_alter_service_index_and_ordering.py b/netbox/ipam/migrations/0091_alter_service_index_and_ordering.py new file mode 100644 index 000000000..aa91e863c --- /dev/null +++ b/netbox/ipam/migrations/0091_alter_service_index_and_ordering.py @@ -0,0 +1,58 @@ +from django.db import migrations, models + + +def populate__ports_lowest(apps, schema_editor): + Service = apps.get_model('ipam', 'Service') + ServiceTemplate = apps.get_model('ipam', 'ServiceTemplate') + CHUNK_SIZE = 500 + + for model in (Service, ServiceTemplate): + chunk = [] + qs = model.objects.filter(_ports_lowest__isnull=True).only('id', 'ports', '_ports_lowest') + for obj in qs.iterator(chunk_size=CHUNK_SIZE): + if obj.ports: + obj._ports_lowest = min(obj.ports) + chunk.append(obj) + if len(chunk) >= CHUNK_SIZE: + model.objects.bulk_update(chunk, ['_ports_lowest']) + chunk = [] + if chunk: + model.objects.bulk_update(chunk, ['_ports_lowest']) + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0090_vlangroup_recompute_total_vlan_ids'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='service', + name='ipam_servic_protoco_687d13_idx', + ), + migrations.AddField( + model_name='service', + name='_ports_lowest', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='servicetemplate', + name='_ports_lowest', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.RunPython(populate__ports_lowest, migrations.RunPython.noop), + migrations.AddIndex( + model_name='service', + index=models.Index( + fields=['protocol', '_ports_lowest', 'id'], + name='ipam_servic_protoco_e2901d_idx' + ), + ), + migrations.AlterModelOptions( + name='service', + options={ + 'ordering': ('protocol', '_ports_lowest', 'id') + }, + ), + ] diff --git a/netbox/ipam/migrations/0092_iprange_host_indexes.py b/netbox/ipam/migrations/0092_iprange_host_indexes.py new file mode 100644 index 000000000..76065bd27 --- /dev/null +++ b/netbox/ipam/migrations/0092_iprange_host_indexes.py @@ -0,0 +1,34 @@ +import django.db.models.functions.comparison +from django.db import migrations, models + +import ipam.fields +import ipam.lookups + + +class Migration(migrations.Migration): + dependencies = [ + ('ipam', '0091_alter_service_index_and_ordering'), + ] + + operations = [ + migrations.AddIndex( + model_name='iprange', + index=models.Index( + django.db.models.functions.comparison.Cast( + ipam.lookups.Host('start_address'), + output_field=ipam.fields.IPAddressField(), + ), + name='ipam_iprange_start_host', + ), + ), + migrations.AddIndex( + model_name='iprange', + index=models.Index( + django.db.models.functions.comparison.Cast( + ipam.lookups.Host('end_address'), + output_field=ipam.fields.IPAddressField(), + ), + name='ipam_iprange_end_host', + ), + ), + ] diff --git a/netbox/ipam/migrations/0091_denormalization_triggers.py b/netbox/ipam/migrations/0093_denormalization_triggers.py similarity index 89% rename from netbox/ipam/migrations/0091_denormalization_triggers.py rename to netbox/ipam/migrations/0093_denormalization_triggers.py index e92b2a41c..902f7f294 100644 --- a/netbox/ipam/migrations/0091_denormalization_triggers.py +++ b/netbox/ipam/migrations/0093_denormalization_triggers.py @@ -10,7 +10,7 @@ from utilities.migration import cached_scope_triggers class Migration(migrations.Migration): dependencies = [ - ('ipam', '0090_vlangroup_recompute_total_vlan_ids'), + ('ipam', '0092_iprange_host_indexes'), # Source tables (dcim_site, dcim_location) must already exist. ('dcim', '0238_ltree_paths'), ] diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index cd4554257..51941526e 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -15,7 +15,7 @@ from ipam.constants import * from ipam.fields import IPAddressField, IPNetworkField from ipam.lookups import Host from ipam.managers import IPAddressManager -from ipam.querysets import PrefixQuerySet +from ipam.querysets import IPRangeQuerySet, PrefixQuerySet from ipam.validators import DNSValidator from netbox.config import get_config from netbox.models import OrganizationalModel, PrimaryModel @@ -425,14 +425,63 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary return Prefix.objects.filter(prefix__net_contained=str(self.prefix)) return Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf) + @property + def usable_ip_bounds(self): + """ + Return the first and last IPs considered usable for available-IP calculations. + + Pools and IPv4 /31-/32 / IPv6 /127-/128 are fully usable; otherwise IPv4 excludes + network and broadcast, IPv6 excludes the subnet-router anycast address. + """ + network = netaddr.IPNetwork(self.prefix) + family = network.version + first = network.first + last = network.last + mask_length = network.prefixlen + + if ( + self.is_pool + or (family == 4 and mask_length >= 31) + or (family == 6 and mask_length >= 127) + ): + return ( + netaddr.IPAddress(first, version=family), + netaddr.IPAddress(last, version=family), + ) + + if family == 4: + return ( + netaddr.IPAddress(first + 1, version=family), + netaddr.IPAddress(last - 1, version=family), + ) + + return ( + netaddr.IPAddress(first + 1, version=family), + netaddr.IPAddress(last, version=family), + ) + + @property + def usable_size(self): + """ + The number of usable host addresses within the prefix (excludes reserved addresses). + """ + first_ip, last_ip = self.usable_ip_bounds + return int(last_ip) - int(first_ip) + 1 + def get_child_ranges(self, **kwargs): """ Return all IPRanges within this Prefix and VRF. """ + # A host BETWEEN over the prefix span uses the ipam_iprange_*_host btree indexes. + prefix = netaddr.IPNetwork(self.prefix) + bounds = ( + netaddr.IPAddress(prefix.first, version=prefix.version), + netaddr.IPAddress(prefix.last, version=prefix.version), + ) return IPRange.objects.filter( vrf=self.vrf, - start_address__net_host_contained=str(self.prefix), - end_address__net_host_contained=str(self.prefix), + start_address__host_between=bounds, + end_address__host_between=bounds, **kwargs ) @@ -441,52 +490,106 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary Return all IPAddresses within this Prefix and VRF. If this Prefix is a container in the global table, return child IPAddresses belonging to any VRF. """ + # A host BETWEEN over the prefix span is index-sargable without the <<= containment recheck. + prefix = netaddr.IPNetwork(self.prefix) + bounds = ( + netaddr.IPAddress(prefix.first, version=prefix.version), + netaddr.IPAddress(prefix.last, version=prefix.version), + ) if self.vrf is None and self.status == PrefixStatusChoices.STATUS_CONTAINER: - return IPAddress.objects.filter(address__net_host_contained=str(self.prefix)) - return IPAddress.objects.filter(address__net_host_contained=str(self.prefix), vrf=self.vrf) + return IPAddress.objects.filter(address__host_between=bounds) + return IPAddress.objects.filter(address__host_between=bounds, vrf=self.vrf) def get_available_ips(self): """ Return all available IPs within this prefix as an IPSet. """ - prefix = netaddr.IPSet(self.prefix) - child_ips = netaddr.IPSet([ - ip.address.ip for ip in self.get_child_ips() - ]) - child_ranges = netaddr.IPSet([ - iprange.range for iprange in self.get_child_ranges().filter(mark_populated=True) - ]) - available_ips = prefix - child_ips - child_ranges + return netaddr.IPSet( + cidr + for start, end in self._available_intervals() + for cidr in netaddr.iprange_to_cidrs(start, end) + ) - # Pool, IPv4 /31-/32 or IPv6 /127-/128 sets are fully usable - if ( - self.is_pool - or (self.family == 4 and self.prefix.prefixlen >= 31) - or (self.family == 6 and self.prefix.prefixlen >= 127) - ): - return available_ips + def iter_available_ips(self): + """ + Yield the available IPs within this prefix as netaddr.IPAddress objects, in + ascending order. Unlike get_available_ips(), consumption is lazy: stopping + early stops reading from the database. + """ + for start, end in self._available_intervals(): + yield from netaddr.iter_iprange(start, end) - if self.family == 4: - # For "normal" IPv4 prefixes, omit first and last addresses - available_ips -= netaddr.IPSet([ - netaddr.IPAddress(self.prefix.first), - netaddr.IPAddress(self.prefix.last), - ]) - else: - # For IPv6 prefixes, omit the Subnet-Router anycast address - # per RFC 4291 - available_ips -= netaddr.IPSet([netaddr.IPAddress(self.prefix.first)]) + def get_available_ip_count(self): + """ + Return the number of available IPs within the prefix. + """ + first_ip, last_ip = self.usable_ip_bounds + usable_size = int(last_ip) - int(first_ip) + 1 - return available_ips + populated_intervals = self.get_child_ranges(mark_populated=True).get_intervals(first_ip, last_ip) + populated_count = sum(int(end) - int(start) + 1 for start, end in populated_intervals) + + # Populated ranges already cover the usable span; skip the child-IP count entirely. + if populated_count >= usable_size: + return 0 + + child_ip_count = ( + self.get_child_ips() + .filter(address__host_between=(first_ip, last_ip)) + .count_distinct_hosts(exclude_intervals=populated_intervals) + ) + + return max(usable_size - populated_count - child_ip_count, 0) + + def get_ip_usage_summary(self): + """ + Return the available IP count and utilization together as a dict, sharing a + single distinct-host scan. Intended for detail views rendering both values; + list views should call get_utilization() alone, which is cheaper per row. + """ + # Marked-utilized and container utilization need no host scan; delegate. + if self.mark_utilized or self.status == PrefixStatusChoices.STATUS_CONTAINER: + return { + 'available_ip_count': self.get_available_ip_count(), + 'utilization': self.get_utilization(), + } + + first_ip, last_ip = self.usable_ip_bounds + usable_size = int(last_ip) - int(first_ip) + 1 + + populated_intervals = self.get_child_ranges(mark_populated=True).get_intervals(first_ip, last_ip) + utilized_intervals = self.get_child_ranges(mark_utilized=True).get_intervals() + + counts = self.get_child_ips().count_distinct_hosts_pair( + bounds=(first_ip, last_ip), + bounded_exclude=populated_intervals, + total_exclude=utilized_intervals, + ) + + populated_count = sum(int(end) - int(start) + 1 for start, end in populated_intervals) + utilized_range_count = sum(int(end) - int(start) + 1 for start, end in utilized_intervals) + + prefix_size = self._get_utilization_denominator() + + return { + 'available_ip_count': max(usable_size - populated_count - counts['bounded'], 0), + 'utilization': min(float(utilized_range_count + counts['total']) / prefix_size * 100, 100), + } def get_first_available_ip(self): """ Return the first available IP within the prefix (or None). """ - available_ips = self.get_available_ips() - if not available_ips: + first_ip, last_ip = self.usable_ip_bounds + populated_intervals = self.get_child_ranges(mark_populated=True).get_intervals(first_ip, last_ip) + + first_available_ip = self.get_child_ips().first_available_host( + first_ip, last_ip, exclude_intervals=populated_intervals, + ) + + if first_available_ip is None: return None - return '{}/{}'.format(next(available_ips.__iter__()), self.prefix.prefixlen) + return f'{first_available_ip}/{self.prefix.prefixlen}' def get_utilization(self): """ @@ -504,20 +607,43 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary child_prefixes = netaddr.IPSet([p.prefix for p in queryset]) utilization = float(child_prefixes.size) / self.prefix.size * 100 else: - # Compile an IPSet to avoid counting duplicate IPs - child_ips = netaddr.IPSet() - for iprange in self.get_child_ranges().filter(mark_utilized=True): - child_ips.add(iprange.range) - for ip in self.get_child_ips(): - child_ips.add(ip.address.ip) + prefix_size = self._get_utilization_denominator() + utilized_intervals = self.get_child_ranges(mark_utilized=True).get_intervals() + utilized_range_count = sum(int(end) - int(start) + 1 for start, end in utilized_intervals) - prefix_size = self.prefix.size - if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool: - prefix_size -= 2 - utilization = float(child_ips.size) / prefix_size * 100 + # Utilized ranges already saturate the prefix; skip the child-IP count. + if utilized_range_count >= prefix_size: + return 100 + + child_ip_count = self.get_child_ips().count_distinct_hosts( + exclude_intervals=utilized_intervals, + ) + + utilization = float(utilized_range_count + child_ip_count) / prefix_size * 100 return min(utilization, 100) + def _available_intervals(self): + """ + Yield the available (start, end) host intervals within the prefix. + """ + first_ip, last_ip = self.usable_ip_bounds + populated_intervals = self.get_child_ranges(mark_populated=True).get_intervals(first_ip, last_ip) + + return self.get_child_ips().available_intervals( + first_ip, last_ip, exclude_intervals=populated_intervals, + ) + + def _get_utilization_denominator(self): + """ + The address count utilization is measured against (IPv4 non-pool prefixes + exclude the network and broadcast addresses; IPv6 uses the full prefix size). + """ + prefix_size = self.prefix.size + if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool: + return prefix_size - 2 + return prefix_size + class IPRange(ContactsMixin, PrimaryModel): """ @@ -576,12 +702,24 @@ class IPRange(ContactsMixin, PrimaryModel): help_text=_("Report space as fully utilized") ) + objects = IPRangeQuerySet.as_manager() + clone_fields = ( 'vrf', 'tenant', 'status', 'role', 'description', 'mark_populated', 'mark_utilized', ) class Meta: ordering = (F('vrf').asc(nulls_first=True), 'start_address', 'pk') # (vrf, start_address) may be non-unique + indexes = ( + models.Index( + Cast(Host('start_address'), output_field=IPAddressField()), + name='ipam_iprange_start_host', + ), + models.Index( + Cast(Host('end_address'), output_field=IPAddressField()), + name='ipam_iprange_end_host', + ), + ) verbose_name = _('IP range') verbose_name_plural = _('IP ranges') @@ -709,40 +847,14 @@ class IPRange(ContactsMixin, PrimaryModel): def get_status_color(self): return IPRangeStatusChoices.colors.get(self.status) - def get_child_ips(self): - """ - Return all IPAddresses within this IPRange and VRF. - """ - return IPAddress.objects.filter( - address__gte=self.start_address, - address__lte=self.end_address, - vrf=self.vrf - ) - - def get_available_ips(self): - """ - Return all available IPs within this range as an IPSet. - """ - if self.mark_populated: - return netaddr.IPSet() - - range = netaddr.IPRange(self.start_address.ip, self.end_address.ip) - child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]) - - return netaddr.IPSet(range) - child_ips - @cached_property def first_available_ip(self): """ Return the first available IP within the range (or None). """ - available_ips = self.get_available_ips() - if not available_ips: - return None + return self.get_first_available_ip() - return '{}/{}'.format(next(available_ips.__iter__()), self.start_address.prefixlen) - - @cached_property + @property def utilization(self): """ Determine the utilization of the range and return it as a percentage. @@ -750,12 +862,79 @@ class IPRange(ContactsMixin, PrimaryModel): if self.mark_utilized: return 100 - # Compile an IPSet to avoid counting duplicate IPs - child_count = netaddr.IPSet([ - ip.address.ip for ip in self.get_child_ips() - ]).size + return min(float(self._occupied_host_count) / self.size * 100, 100) - return min(float(child_count) / self.size * 100, 100) + def get_child_ips(self): + """ + Return all IPAddresses within this IPRange and VRF. + """ + return IPAddress.objects.filter( + vrf=self.vrf, + address__host_between=(self.start_address.ip, self.end_address.ip), + ) + + def get_available_ips(self): + """ + Return all available IPs within this range as an IPSet. + """ + return netaddr.IPSet( + cidr + for start, end in self._available_intervals() + for cidr in netaddr.iprange_to_cidrs(start, end) + ) + + def iter_available_ips(self): + """ + Yield the available IPs within this range as netaddr.IPAddress objects, in + ascending order. Unlike get_available_ips(), consumption is lazy: stopping + early stops reading from the database. + """ + for start, end in self._available_intervals(): + yield from netaddr.iter_iprange(start, end) + + def get_available_ip_count(self): + """ + Return the number of available IPs within the range. + """ + if self.mark_populated: + return 0 + + return max(self.size - self._occupied_host_count, 0) + + def get_first_available_ip(self): + """ + Return the first available IP within the range (or None). + """ + if self.mark_populated: + return None + + first_available_ip = self.get_child_ips().first_available_host( + self.start_address.ip, self.end_address.ip, + ) + + if first_available_ip is None: + return None + + return f'{first_available_ip}/{self.start_address.prefixlen}' + + def _available_intervals(self): + """ + Yield the available (start, end) host intervals within the range. + """ + if self.mark_populated: + return iter(()) + + return self.get_child_ips().available_intervals( + self.start_address.ip, self.end_address.ip, + ) + + @cached_property + def _occupied_host_count(self): + """ + The number of distinct occupied hosts within the range, cached for the + lifetime of the instance. + """ + return self.get_child_ips().count_distinct_hosts() class IPAddress(ContactsMixin, PrimaryModel): @@ -948,10 +1127,10 @@ class IPAddress(ContactsMixin, PrimaryModel): # Disallow the creation of IPAddresses within an IPRange with mark_populated=True parent_range_qs = IPRange.objects.filter( - start_address__lte=self.address, - end_address__gte=self.address, + start_address__host__inet__lte=self.address.ip, + end_address__host__inet__gte=self.address.ip, vrf=self.vrf, - mark_populated=True + mark_populated=True, ) if not self.pk and (parent_range := parent_range_qs.first()): raise ValidationError({ diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index d934465c3..ee12e3b04 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -31,10 +31,22 @@ class ServiceBase(models.Model): ), verbose_name=_('port numbers') ) + _ports_lowest = models.PositiveIntegerField( + null=True, + blank=True, + ) class Meta: abstract = True + def save(self, *args, **kwargs): + # On saving find the smallest port and save for default ordering + self._ports_lowest = min(self.ports) if self.ports else None + update_fields = kwargs.get('update_fields') + if update_fields is not None and '_ports_lowest' not in update_fields: + kwargs['update_fields'] = list(update_fields) + ['_ports_lowest'] + super().save(*args, **kwargs) + def __str__(self): return f'{self.name} ({self.get_protocol_display()}/{self.port_list})' @@ -74,7 +86,6 @@ class Service(ContactsMixin, ServiceBase, PrimaryModel): ct_field='parent_object_type', fk_field='parent_object_id' ) - name = models.CharField( max_length=100, verbose_name=_('name') @@ -93,9 +104,9 @@ class Service(ContactsMixin, ServiceBase, PrimaryModel): class Meta: indexes = ( - models.Index(fields=('protocol', 'ports', 'id')), # Default ordering + models.Index(fields=('protocol', '_ports_lowest', 'id')), # Default ordering models.Index(fields=('parent_object_type', 'parent_object_id')), ) - ordering = ('protocol', 'ports', 'pk') # (protocol, port) may be non-unique + ordering = ('protocol', '_ports_lowest', 'id') verbose_name = _('application service') verbose_name_plural = _('application services') diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index 2ed3512df..40f56f87f 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -330,7 +330,7 @@ class VLAN(PrimaryModel): ) # Check that the VLAN ID is permitted in the assigned group (if any) - if self.group: + if self.group and self.vid is not None: if not any([self.vid in r for r in self.group.vid_ranges]): raise ValidationError({ 'vid': _( diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index 12bd8328c..4a48ee81d 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -1,18 +1,51 @@ +import heapq + +import netaddr from django.contrib.contenttypes.models import ContentType from django.db.models import Count, F, OuterRef, Q, Subquery, Value from django.db.models.expressions import RawSQL -from django.db.models.functions import NullIf, Round +from django.db.models.functions import Cast, NullIf, Round from utilities.query import count_related from utilities.querysets import RestrictedQuerySet +from .fields import IPAddressField +from .lookups import Host + __all__ = ( 'ASNRangeQuerySet', + 'IPAddressQuerySet', + 'IPRangeQuerySet', 'PrefixQuerySet', 'VLANGroupQuerySet', 'VLANQuerySet', ) +# The host portion of an IP address (mask ignored), in the same form as the +# ipam_ipaddress_host expression index. +HOST_ADDRESS = Cast(Host('address'), output_field=IPAddressField()) + + +def _merge_intervals(intervals): + """ + Return the union of (start, end) netaddr.IPAddress intervals, merged and sorted. + """ + if not intervals: + return [] + + intervals = sorted(intervals) + merged = [intervals[0]] + + for start, end in intervals[1:]: + current_start, current_end = merged[-1] + # Adjacency math in int space; netaddr raises at the address-space maximum. + if start.version == current_end.version and int(start) <= int(current_end) + 1: + merged[-1] = (current_start, max(current_end, end)) + else: + merged.append((start, end)) + + return merged + class ASNRangeQuerySet(RestrictedQuerySet): @@ -32,6 +65,162 @@ class ASNRangeQuerySet(RestrictedQuerySet): return self.annotate(asn_count=Subquery(asns)) +class IPAddressQuerySet(RestrictedQuerySet): + + def count_distinct_hosts(self, exclude_intervals=()): + """ + Count distinct host addresses, optionally excluding (start, end) netaddr.IPAddress intervals. + """ + queryset = self + for start, end in exclude_intervals: + queryset = queryset.exclude(address__host_between=(start, end)) + + return queryset.aggregate(count=Count(HOST_ADDRESS, distinct=True))['count'] + + def count_distinct_hosts_pair(self, bounds, bounded_exclude=(), total_exclude=()): + """ + Return two distinct host counts computed in a single scan, as a dict: + 'bounded' counts hosts within the (first_ip, last_ip) bounds excluding the + bounded_exclude intervals; 'total' counts all hosts excluding the + total_exclude intervals. Interval arguments match the output of + IPRangeQuerySet.get_intervals(). Avoids a second scan of the host expression + index when both counts are needed. Use only when both counts are needed (e.g. + Prefix.get_ip_usage_summary()); single-purpose callers should prefer + count_distinct_hosts(). + """ + # The deduplicated column is already a bare host; plain comparisons beat + # the host_between lookup here, which would re-wrap it in HOST()::inet. + bounded_q = Q(host_address__range=(str(bounds[0]), str(bounds[1]))) + for start, end in bounded_exclude: + bounded_q &= ~Q(host_address__range=(str(start), str(end))) + total_q = Q() + for start, end in total_exclude: + total_q &= ~Q(host_address__range=(str(start), str(end))) + + hosts = self.order_by().annotate(host_address=HOST_ADDRESS).values('host_address').distinct() + return hosts.aggregate( + bounded=Count('host_address', filter=bounded_q), + # An empty Q is falsy; fall back to a plain count of all hosts. + total=Count('host_address', filter=total_q or None), + ) + + def _iter_distinct_hosts(self, first_ip, last_ip, batch_size): + """ + Yield the distinct occupied hosts in [first_ip, last_ip] in ascending order, + fetched in LIMIT batches that resume just past the last seen host. (A + server-side cursor is unsuitable here: on autocommit connections Django + declares it WITH HOLD, which materializes the full result at DECLARE.) + """ + resume = first_ip + while True: + # order_by() first clears the default ordering, which would otherwise + # leak into SELECT and break distinct(). + hosts = list( + self.filter(address__host_between=(resume, last_ip)) + .order_by() + .annotate(host_address=HOST_ADDRESS) + .values_list('host_address', flat=True) + .distinct() + .order_by('host_address')[:batch_size] + ) + for host in hosts: + yield host.ip + if len(hosts) < batch_size: + return + last_host = hosts[-1].ip + if int(last_host) >= int(last_ip): + return + resume = netaddr.IPAddress(int(last_host) + 1, version=last_host.version) + + def available_intervals(self, first_ip, last_ip, exclude_intervals=(), batch_size=5000): + """ + Yield the unoccupied (start, end) netaddr.IPAddress intervals (inclusive) + within [first_ip, last_ip], in ascending order. exclude_intervals are + (start, end) netaddr.IPAddress pairs; they are merged and sorted internally, + intervals of a foreign address family are ignored, and addresses they cover + count as occupied. Consumption is lazy: a caller that stops early stops + fetching host batches. + """ + if batch_size < 1: + raise ValueError('batch_size must be greater than zero') + + first_int, last_int = int(first_ip), int(last_ip) + version = first_ip.version + + if first_int > last_int: + return + # Normalize: the sweep below requires sorted, non-overlapping, same-family intervals. + exclude_intervals = _merge_intervals([ + (start, end) + for start, end in exclude_intervals + if start.version == end.version == version + ]) + intervals = [(int(start), int(end)) for start, end in exclude_intervals] + + # Fast path: one merged excluded interval covers the entire span. + if intervals and intervals[0][0] <= first_int and intervals[0][1] >= last_int: + return + + hosts = ( + (int(host), int(host)) + for host in self._iter_distinct_hosts(first_ip, last_ip, batch_size) + ) + + candidate = first_int + # Ties on `start` are harmless; the sweep handles overlapping intervals. + for start, end in heapq.merge(intervals, hosts): + if end < candidate: + continue + if start > candidate: + yield ( + netaddr.IPAddress(candidate, version=version), + netaddr.IPAddress(min(start - 1, last_int), version=version), + ) + candidate = max(candidate, end + 1) + if candidate > last_int: + return + + if candidate <= last_int: + yield ( + netaddr.IPAddress(candidate, version=version), + netaddr.IPAddress(last_int, version=version), + ) + + def first_available_host(self, first_ip, last_ip, exclude_intervals=()): + """ + Return the first host in [first_ip, last_ip] neither present nor in an excluded interval (or None). + """ + interval = next(self.available_intervals(first_ip, last_ip, exclude_intervals), None) + return interval[0] if interval else None + + +class IPRangeQuerySet(RestrictedQuerySet): + + def get_intervals(self, first_ip=None, last_ip=None): + """ + Return ranges as merged (start, end) netaddr.IPAddress intervals, optionally clipped to the bounds. + """ + intervals = [] + + # order_by() clears the default ordering; _merge_intervals() sorts anyway. + for start_address, end_address in self.order_by().values_list('start_address', 'end_address'): + start, end = start_address.ip, end_address.ip + + if first_ip is not None: + if end < first_ip: + continue + start = max(start, first_ip) + + if last_ip is not None: + if start > last_ip: + continue + end = min(end, last_ip) + + intervals.append((start, end)) + + return _merge_intervals(intervals) + + class PrefixQuerySet(RestrictedQuerySet): def annotate_hierarchy(self): diff --git a/netbox/ipam/tests/test_fields.py b/netbox/ipam/tests/test_fields.py new file mode 100644 index 000000000..b7bcd3b28 --- /dev/null +++ b/netbox/ipam/tests/test_fields.py @@ -0,0 +1,29 @@ +from django.test import TestCase +from netaddr import IPAddress + +from ipam.fields import IPAddressField, IPNetworkField + + +class BaseIPFieldTestCase(TestCase): + """ + Regression coverage for BaseIPField.get_prep_value() — zero addresses such as + 0.0.0.0 and :: are valid hosts and must not be treated as empty values. + """ + + def test_get_prep_value_accepts_ipv4_zero_address(self): + # Regression: 0.0.0.0 is a valid host, not an empty value. + self.assertEqual(IPAddressField().get_prep_value(IPAddress('0.0.0.0')), '0.0.0.0') + + def test_get_prep_value_accepts_ipv6_zero_address(self): + # Regression: :: is a valid host, not an empty value. + self.assertEqual(IPAddressField().get_prep_value(IPAddress('::')), '::') + + def test_get_prep_value_passes_through_empty(self): + self.assertIsNone(IPNetworkField().get_prep_value(None)) + self.assertIsNone(IPAddressField().get_prep_value('')) + + def test_get_prep_value_preserves_raw_zero_as_empty(self): + # Raw int 0 is preserved as the legacy "empty" sentinel; Django's ORM never + # passes it directly, but the previous `not value` check returned None for it. + self.assertIsNone(IPAddressField().get_prep_value(0)) + self.assertIsNone(IPNetworkField().get_prep_value(0)) diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index 900436d62..4e396a9f8 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -4,7 +4,7 @@ from django.test import TestCase from netaddr import IPNetwork from circuits.models import Provider -from dcim.choices import InterfaceTypeChoices +from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Rack, Region, Site, SiteGroup from ipam.choices import * from ipam.filtersets import * @@ -2206,11 +2206,50 @@ class VLANTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'interface_id': interface_id} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + # An interface untagged on one VLAN and tagged on a different VLAN should return both (UNION across paths) + vlans = self.queryset.all()[:2] + interface = Interface.objects.create( + device=Device.objects.first(), + name='Interface X', + type=InterfaceTypeChoices.TYPE_1GE_FIXED, + mode=InterfaceModeChoices.MODE_TAGGED, + untagged_vlan=vlans[0], + ) + interface.tagged_vlans.add(vlans[1]) + params = {'interface_id': interface.pk} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + # A VLAN that is both untagged and tagged on the same interface should be returned only once (deduplication) + interface.tagged_vlans.add(vlans[0]) + params = {'interface_id': interface.pk} + qs = self.filterset(params, self.queryset).qs + self.assertEqual(qs.count(), 2) + self.assertEqual(len(qs), len(set(qs.values_list('pk', flat=True)))) + def test_vminterface(self): vminterface_id = VMInterface.objects.first().pk params = {'vminterface_id': vminterface_id} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + # A VM interface untagged on one VLAN and tagged on a different VLAN should return both (UNION across paths) + vlans = self.queryset.all()[:2] + vminterface = VMInterface.objects.create( + virtual_machine=VirtualMachine.objects.first(), + name='VM Interface X', + mode=InterfaceModeChoices.MODE_TAGGED, + untagged_vlan=vlans[0], + ) + vminterface.tagged_vlans.add(vlans[1]) + params = {'vminterface_id': vminterface.pk} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + # A VLAN that is both untagged and tagged on the same interface should be returned only once (deduplication) + vminterface.tagged_vlans.add(vlans[0]) + params = {'vminterface_id': vminterface.pk} + qs = self.filterset(params, self.queryset).qs + self.assertEqual(qs.count(), 2) + self.assertEqual(len(qs), len(set(qs.values_list('pk', flat=True)))) + def test_qinq_role(self): params = {'qinq_role': [VLANQinQRoleChoices.ROLE_SERVICE, VLANQinQRoleChoices.ROLE_CUSTOMER]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) diff --git a/netbox/ipam/tests/test_forms.py b/netbox/ipam/tests/test_forms.py index 621afc6a8..33bdfb627 100644 --- a/netbox/ipam/tests/test_forms.py +++ b/netbox/ipam/tests/test_forms.py @@ -3,7 +3,7 @@ from django.test import TestCase from dcim.constants import InterfaceTypeChoices from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Region, Site, SiteGroup -from ipam.forms import PrefixForm +from ipam.forms import PrefixForm, VLANIDBulkCreateForm from ipam.forms.bulk_import import IPAddressImportForm @@ -96,3 +96,31 @@ class IPAddressImportFormTestCase(TestCase): self.device.refresh_from_db() self.assertEqual(self.device.oob_ip, ip1, "OOB IP was incorrectly cleared by a row with is_oob=False") + + +class VLANFormTestCase(TestCase): + + def test_bulk_create_valid_patterns(self): + """Single values, ranges, and combinations expand to sorted, deduplicated VLAN IDs.""" + cases = ( + ('100', [100]), + ('5,10,20', [5, 10, 20]), + ('10-20', list(range(10, 21))), + ('1,10-20,300-305', [1, *range(10, 21), *range(300, 306)]), + (' 5 , 7 - 9 ', [5, 7, 8, 9]), + ('5,5,4-6', [4, 5, 6]), + ) + for pattern, expected in cases: + with self.subTest(pattern=pattern): + form = VLANIDBulkCreateForm({'pattern': pattern}) + self.assertTrue(form.is_valid(), form.errors) + self.assertEqual(form.cleaned_data['pattern'], expected) + + def test_bulk_create_invalid_patterns(self): + """Malformed, descending, or out-of-range patterns are rejected with an error on the pattern field.""" + cases = ('', 'abc', '10,abc', '20-10', '10-', '5,', '-5', '0', '4095') + for pattern in cases: + with self.subTest(pattern=pattern): + form = VLANIDBulkCreateForm({'pattern': pattern}) + self.assertFalse(form.is_valid()) + self.assertIn('pattern', form.errors) diff --git a/netbox/ipam/tests/test_lookups.py b/netbox/ipam/tests/test_lookups.py index c23c910cf..2280073ac 100644 --- a/netbox/ipam/tests/test_lookups.py +++ b/netbox/ipam/tests/test_lookups.py @@ -1,7 +1,9 @@ +import netaddr from django.db.backends.postgresql.psycopg_any import NumericRange from django.test import TestCase +from netaddr import IPNetwork -from ipam.models import VLANGroup +from ipam.models import IPAddress, VLANGroup class VLANGroupRangeContainsLookupTestCase(TestCase): @@ -65,3 +67,134 @@ class VLANGroupRangeContainsLookupTestCase(TestCase): specific condition. """ self.assertFalse(VLANGroup.objects.filter(pk=self.g_empty.pk, vid_ranges__range_contains=1).exists()) + + +class IPAddressHostBetweenLookupTestCase(TestCase): + @classmethod + def setUpTestData(cls): + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.0/24')), + IPAddress(address=IPNetwork('192.0.2.1/24')), + IPAddress(address=IPNetwork('192.0.2.5/32')), + IPAddress(address=IPNetwork('192.0.2.10/25')), + IPAddress(address=IPNetwork('192.0.2.11/24')), + IPAddress(address=IPNetwork('2001:db8::1/64')), + IPAddress(address=IPNetwork('2001:db8::5/128')), + IPAddress(address=IPNetwork('2001:db8::10/64')), + )) + + def test_ipv4_boundaries_inclusive(self): + """ + Tests that both bounds are included and hosts outside the window are excluded. + """ + queryset = IPAddress.objects.filter( + address__host_between=(netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10')) + ) + self.assertEqual( + sorted(str(ip.address) for ip in queryset), + ['192.0.2.1/24', '192.0.2.10/25', '192.0.2.5/32'], + ) + + def test_mask_insensitive(self): + """ + Tests that hosts match regardless of their mask length. + """ + queryset = IPAddress.objects.filter( + address__host_between=(netaddr.IPAddress('192.0.2.5'), netaddr.IPAddress('192.0.2.5')) + ) + self.assertEqual(queryset.count(), 1) + + def test_ipv6(self): + """ + Tests that IPv6 hosts filter by host portion. + """ + queryset = IPAddress.objects.filter( + address__host_between=(netaddr.IPAddress('2001:db8::1'), netaddr.IPAddress('2001:db8::5')) + ) + self.assertEqual(queryset.count(), 2) + + def test_bounds_mask_stripped(self): + """ + Tests that bounds supplied with a mask compare by host portion only. + """ + queryset = IPAddress.objects.filter( + address__host_between=(IPNetwork('192.0.2.1/24'), IPNetwork('192.0.2.10/24')) + ) + self.assertEqual(queryset.count(), 3) + + def test_invalid_bounds_raise(self): + """ + Tests that a bounds value which is not a two-item pair raises ValueError. + """ + with self.assertRaises(ValueError): + IPAddress.objects.filter(address__host_between=(netaddr.IPAddress('192.0.2.1'),)) + + def test_invalid_bound_value_raises(self): + """ + Tests that a bound which is not a valid IP address raises ValueError. + """ + with self.assertRaises(ValueError): + IPAddress.objects.filter(address__host_between=('invalid', netaddr.IPAddress('192.0.2.10'))) + + def test_mixed_family_bounds_raise(self): + """ + Tests that bounds from different address families raise ValueError. + """ + with self.assertRaises(ValueError): + IPAddress.objects.filter( + address__host_between=(netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('2001:db8::1')) + ) + + def test_sql_uses_cast_host_expression(self): + """ + Tests that the compiled SQL matches the ipam_ipaddress_host index expression. + """ + queryset = IPAddress.objects.filter( + address__host_between=(netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10')) + ) + self.assertIn('CAST(HOST(', str(queryset.query)) + + +class IPAddressNetLookupsTestCase(TestCase): + @classmethod + def setUpTestData(cls): + IPAddress.objects.bulk_create(( + IPAddress(address='10.0.0.1/24'), + IPAddress(address='10.0.0.2/24'), + IPAddress(address='10.0.0.1/25'), # Same host as the first, different mask + IPAddress(address='2001:db8::1/64'), + )) + + def test_net_host_matches_host_ignoring_mask(self): + """net_host matches every address whose host portion equals the value.""" + qs = IPAddress.objects.filter(address__net_host='10.0.0.1') + self.assertEqual(qs.count(), 2) + + def test_net_host_predicate_is_inet_typed(self): + """net_host casts the host expression to inet so the inet host index applies.""" + sql = str(IPAddress.objects.filter(address__net_host='10.0.0.1').query) + self.assertIn('CAST(HOST(', sql) + self.assertIn('AS INET) =', sql) + + def test_net_in_without_mask(self): + """net_in matches host values supplied without a mask.""" + qs = IPAddress.objects.filter(address__net_in=['10.0.0.1', '10.0.0.2']) + self.assertEqual(qs.count(), 3) + + def test_net_in_with_mask(self): + """net_in matches an exact address/mask value.""" + qs = IPAddress.objects.filter(address__net_in=['10.0.0.1/25']) + self.assertEqual(qs.count(), 1) + + def test_net_in_normalizes_ipv6(self): + """net_in matches an expanded IPv6 form against the canonical host value.""" + qs = IPAddress.objects.filter( + address__net_in=['2001:0db8:0000:0000:0000:0000:0000:0001'] + ) + self.assertEqual(qs.count(), 1) + + def test_net_in_predicate_is_inet_typed(self): + """net_in casts the host expression to inet so the inet host index applies.""" + sql = str(IPAddress.objects.filter(address__net_in=['10.0.0.1']).query) + self.assertIn('CAST(HOST(', sql) + self.assertIn('AS INET) IN', sql) diff --git a/netbox/ipam/tests/test_models.py b/netbox/ipam/tests/test_models.py index e5637433a..b76a7205b 100644 --- a/netbox/ipam/tests/test_models.py +++ b/netbox/ipam/tests/test_models.py @@ -1,3 +1,4 @@ +import netaddr from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db.backends.postgresql.psycopg_any import NumericRange @@ -6,8 +7,11 @@ from netaddr import IPNetwork, IPSet from dcim.models import Site, SiteGroup from ipam.choices import * +from ipam.constants import SERVICE_PORT_MAX, SERVICE_PORT_MIN from ipam.models import * +from ipam.utils import rebuild_prefixes from utilities.data import string_to_ranges +from virtualization.models import VirtualMachine class AggregateTestCase(TestCase): @@ -113,7 +117,7 @@ class IPRangeTestCase(TestCase): self.assertEqual(iprange.size, 1) self.assertEqual(str(iprange), '192.0.2.10-192.0.2.10/24') - self.assertEqual(iprange.first_available_ip, '192.0.2.10/24') + self.assertEqual(iprange.get_first_available_ip(), '192.0.2.10/24') def test_first_available_ip_consumed_single_address_range(self): iprange = IPRange.objects.create( @@ -123,7 +127,7 @@ class IPRangeTestCase(TestCase): IPAddress.objects.create(address=IPNetwork('192.0.2.10/24')) # The sole address in the range is now assigned, so no IPs remain available. - self.assertIsNone(iprange.first_available_ip) + self.assertIsNone(iprange.get_first_available_ip()) def test_single_address_range_ipv6(self): # IPRange.name has IPv4/IPv6-specific formatting; exercise the IPv6 branch @@ -138,7 +142,7 @@ class IPRangeTestCase(TestCase): self.assertEqual(iprange.size, 1) self.assertEqual(str(iprange), '2001:db8::10-2001:db8::10/64') - self.assertEqual(iprange.first_available_ip, '2001:db8::10/64') + self.assertEqual(iprange.get_first_available_ip(), '2001:db8::10/64') def test_reversed_range(self): iprange = IPRange( @@ -165,9 +169,207 @@ class IPRangeTestCase(TestCase): with self.assertRaisesMessage(ValidationError, 'Defined addresses overlap'): iprange.clean() + def test_get_child_ips_host_portion(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('10.0.0.2/24'), + end_address=IPNetwork('10.0.0.254/24'), + ) + + ip1 = IPAddress.objects.create(address=IPNetwork('10.0.0.2/32')) + ip2 = IPAddress.objects.create(address=IPNetwork('10.0.0.3/24')) + + self.assertEqual(set(iprange.get_child_ips()), {ip1, ip2}) + + def test_get_available_ips(self): + """ + Tests that occupied hosts are deduplicated and excluded from the available set. + """ + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.13/24'), + ) + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.10/24')), + IPAddress(address=IPNetwork('192.0.2.10/32')), + )) + + self.assertEqual(iprange.get_available_ips(), IPSet(['192.0.2.11/32', '192.0.2.12/31'])) + + def test_get_available_ips_mark_populated(self): + """ + Tests that a populated range reports no available IPs. + """ + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.13/24'), + mark_populated=True, + ) + + self.assertEqual(iprange.get_available_ips(), IPSet()) + + def test_get_available_ips_vrf(self): + """ + Tests that IPs in other VRFs do not consume range space. + """ + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.11/24'), + vrf=vrf1, + ) + IPAddress.objects.create(address=IPNetwork('192.0.2.10/24'), vrf=vrf2) + + self.assertEqual(iprange.get_available_ips(), IPSet(['192.0.2.10/31'])) + + def test_iter_available_ips(self): + """ + Tests that iter_available_ips() yields the same addresses as get_available_ips() in order. + """ + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.13/24'), + ) + IPAddress.objects.create(address=IPNetwork('192.0.2.11/24')) + + self.assertEqual(list(iprange.iter_available_ips()), sorted(iprange.get_available_ips())) + + def test_available_ip_count(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.12/24')) + + self.assertEqual(iprange.get_available_ip_count(), 9) + + def test_available_ip_count_distinct_hosts(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + ) + + # Two rows for .10 (different masks) must dedupe to a single occupied host. + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.10/24')), + IPAddress(address=IPNetwork('192.0.2.10/32')), + IPAddress(address=IPNetwork('192.0.2.11/24')), + )) + + self.assertEqual(iprange.get_available_ip_count(), 8) + + def test_available_ip_count_vrf(self): + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + vrf=vrf1, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.12/24'), vrf=vrf1) + IPAddress.objects.create(address=IPNetwork('192.0.2.13/24'), vrf=vrf2) + + # Only the VRF 1 IP should count. + self.assertEqual(iprange.get_available_ip_count(), 9) + + def test_available_ip_count_populated(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + mark_populated=True, + ) + + self.assertEqual(iprange.get_available_ip_count(), 0) + + def test_first_available_ip_full(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.11/24'), + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.10/24')) + IPAddress.objects.create(address=IPNetwork('192.0.2.11/24')) + + self.assertIsNone(iprange.get_first_available_ip()) + + def test_first_available_ip_populated(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + mark_populated=True, + ) + + self.assertIsNone(iprange.get_first_available_ip()) + + def test_first_available_ip_ipv6(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('::/126'), + end_address=IPNetwork('::3/126'), + ) + + self.assertEqual(iprange.get_first_available_ip(), '::/126') + + def test_utilization_distinct_hosts(self): + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.10/24')), + IPAddress(address=IPNetwork('192.0.2.10/32')), + IPAddress(address=IPNetwork('192.0.2.11/24')), + )) + + # Two distinct hosts in a 10-address range. + self.assertEqual(iprange.utilization, 2 / 10 * 100) + + def test_utilization_vrf(self): + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + vrf=vrf1, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.12/24'), vrf=vrf1) + IPAddress.objects.create(address=IPNetwork('192.0.2.13/24'), vrf=vrf2) + + # Only the VRF 1 IP counts toward utilization. + self.assertEqual(iprange.utilization, 1 / 10 * 100) + + def test_utilization_duplicate_ips_vrf(self): + """ + Tests that identical IPs in a non-unique VRF count once toward range utilization. + """ + vrf = VRF.objects.create(name='VRF 1', enforce_unique=False) + iprange = IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + vrf=vrf, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.12/24'), vrf=vrf), + IPAddress(address=IPNetwork('192.0.2.12/24'), vrf=vrf), + )) + + self.assertEqual(iprange.utilization, 1 / 10 * 100) + class PrefixTestCase(TestCase): + def assertAvailableIPCountMatchesIPSet(self, prefix): + """ + Confirm that get_available_ip_count() matches get_available_ips().size for the supplied prefix. + """ + self.assertEqual(prefix.get_available_ip_count(), prefix.get_available_ips().size) + def test_family_string(self): # Test property when prefix is a string prefix = Prefix(prefix='10.0.0.0/8') @@ -251,6 +453,23 @@ class PrefixTestCase(TestCase): self.assertEqual(child_ranges[0], ranges[2]) self.assertEqual(child_ranges[1], ranges[3]) + def test_get_child_ranges_other_family(self): + """ + Tests that ranges of a different address family are not returned. + """ + prefix = Prefix.objects.create(prefix=IPNetwork('192.168.0.16/28')) + IPRange.objects.bulk_create(( + IPRange( + start_address=IPNetwork('192.168.0.18/28'), end_address=IPNetwork('192.168.0.20/28'), size=3 + ), + IPRange(start_address=IPNetwork('::1/64'), end_address=IPNetwork('::2/64'), size=2), + )) + + child_ranges = prefix.get_child_ranges() + + self.assertEqual(len(child_ranges), 1) + self.assertEqual(child_ranges[0].start_address, IPNetwork('192.168.0.18/28')) + def test_get_child_ips(self): vrfs = VRF.objects.bulk_create(( VRF(name='VRF 1'), @@ -330,6 +549,319 @@ class PrefixTestCase(TestCase): self.assertEqual(available_ips, missing_ips) + def test_iter_available_ips(self): + """ + Tests that iter_available_ips() yields the same addresses as get_available_ips() in order. + """ + parent_prefix = Prefix.objects.create(prefix=IPNetwork('10.0.0.0/28')) + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('10.0.0.1/28')), + IPAddress(address=IPNetwork('10.0.0.5/28')), + )) + IPRange.objects.create( + start_address=IPNetwork('10.0.0.8/28'), + end_address=IPNetwork('10.0.0.9/28'), + mark_populated=True, + ) + + available_ips = list(parent_prefix.iter_available_ips()) + + self.assertEqual(available_ips, sorted(parent_prefix.get_available_ips())) + self.assertEqual(available_ips[0], netaddr.IPAddress('10.0.0.2')) + self.assertEqual(available_ips[-1], netaddr.IPAddress('10.0.0.14')) + + def test_get_available_ips_ipv6(self): + """ + Tests that the subnet-router anycast address is excluded and the last address included. + """ + parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8::/126')) + IPAddress.objects.create(address=IPNetwork('2001:db8::1/126')) + + self.assertEqual(parent_prefix.get_available_ips(), IPSet(['2001:db8::2/127'])) + + def test_get_available_ips_pool(self): + """ + Tests that pool prefixes include the network and broadcast addresses. + """ + parent_prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/30'), is_pool=True) + IPAddress.objects.create(address=IPNetwork('192.0.2.1/30')) + + self.assertEqual(parent_prefix.get_available_ips(), IPSet(['192.0.2.0/32', '192.0.2.2/31'])) + + def test_available_ip_count_distinct_hosts(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.1/29')), + IPAddress(address=IPNetwork('192.0.2.1/32')), + IPAddress(address=IPNetwork('192.0.2.3/29')), + )) + + # Usable hosts in /29: 6. Two unique hosts occupy .1 and .3. + self.assertEqual(prefix.get_available_ip_count(), 4) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_populated_ranges(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.1/29')), + IPAddress(address=IPNetwork('192.0.2.3/29')), # Inside the populated range; not double-counted. + )) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.3/29'), + end_address=IPNetwork('192.0.2.4/29'), + mark_populated=True, + ) + + # Usable 6, one IP outside the range at .1, populated range covers .3-.4. + # Available: .2, .5, .6. + self.assertEqual(prefix.get_available_ip_count(), 3) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv4_pool(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/30'), + status=PrefixStatusChoices.STATUS_ACTIVE, + is_pool=True, + ) + + self.assertEqual(prefix.get_available_ip_count(), 4) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv4_non_pool(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/30'), + status=PrefixStatusChoices.STATUS_ACTIVE, + is_pool=False, + ) + + self.assertEqual(prefix.get_available_ip_count(), 2) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv4_non_pool_ignores_unusable_ips(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/30'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # Network and broadcast addresses are unusable for non-pool IPv4 prefixes; + # an IP assigned to either must not reduce the available count. + IPAddress.objects.create(address=IPNetwork('192.0.2.0/30')) + IPAddress.objects.create(address=IPNetwork('192.0.2.3/30')) + + self.assertEqual(prefix.get_available_ip_count(), 2) + self.assertEqual(prefix.get_first_available_ip(), '192.0.2.1/30') + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv6(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('2001:db8::/126'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # /126 has 4 addresses; normal IPv6 prefix excludes the first. + self.assertEqual(prefix.get_available_ip_count(), 3) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv6_ignores_subnet_router_anycast(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('2001:db8::/126'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # The subnet-router anycast (::) address is unusable for normal IPv6 prefixes; + # an IP assigned there must not reduce the available count. + IPAddress.objects.create(address=IPNetwork('2001:db8::/126')) + + self.assertEqual(prefix.get_available_ip_count(), 3) + self.assertEqual(prefix.get_first_available_ip(), '2001:db8::1/126') + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv6_127(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('2001:db8::/127'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + self.assertEqual(prefix.get_available_ip_count(), 2) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_ipv6_populated_range(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('2001:db8::/126'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPRange.objects.create( + start_address=IPNetwork('2001:db8::1/126'), + end_address=IPNetwork('2001:db8::2/126'), + mark_populated=True, + ) + + # Usable IPv6 hosts in /126: ::1, ::2, ::3. Populated: ::1-::2. + self.assertEqual(prefix.get_available_ip_count(), 1) + self.assertEqual(prefix.get_first_available_ip(), '2001:db8::3/126') + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_overlapping_ranges(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.1/29'), + end_address=IPNetwork('192.0.2.3/29'), + mark_populated=True, + ) + IPRange.objects.create( + start_address=IPNetwork('192.0.2.2/29'), + end_address=IPNetwork('192.0.2.4/29'), + mark_populated=True, + ) + + # Usable hosts: .1-.6 => 6. Populated union: .1-.4 => 4. Available: .5-.6 => 2. + self.assertEqual(prefix.get_available_ip_count(), 2) + self.assertEqual(prefix.get_first_available_ip(), '192.0.2.5/29') + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_vrf(self): + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + vrf=vrf1, + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.1/29'), vrf=vrf1) + IPAddress.objects.create(address=IPNetwork('192.0.2.2/29'), vrf=vrf2) + + # Usable .1-.6 => 6. Only the VRF 1 IP should count. + self.assertEqual(prefix.get_available_ip_count(), 5) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_vrf_ranges(self): + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + vrf=vrf1, + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # Covers every usable host, but in a different VRF. + IPRange.objects.create( + start_address=IPNetwork('192.0.2.1/29'), + end_address=IPNetwork('192.0.2.6/29'), + vrf=vrf2, + mark_populated=True, + ) + + self.assertEqual(prefix.get_available_ip_count(), 6) + self.assertEqual(prefix.get_first_available_ip(), '192.0.2.1/29') + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_fully_populated(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/30'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # Populated range covers every usable address (.1-.2 in a non-pool /30). + IPRange.objects.create( + start_address=IPNetwork('192.0.2.1/30'), + end_address=IPNetwork('192.0.2.2/30'), + mark_populated=True, + ) + + # Exercises the early-return paths that skip the child-IP count and + # the host-stream iterator entirely. + self.assertEqual(prefix.get_available_ip_count(), 0) + self.assertIsNone(prefix.get_first_available_ip()) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_query_count(self): + """ + Tests that the count runs one interval query plus exactly one host scan. + """ + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24')) + + IPAddress.objects.bulk_create( + IPAddress(address=IPNetwork(f'192.0.2.{i}/24')) for i in range(1, 11) + ) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.20/24'), + end_address=IPNetwork('192.0.2.29/24'), + mark_populated=True, + ) + + with self.assertNumQueries(2): + prefix.get_available_ip_count() + + def test_available_ip_count_container(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_CONTAINER, + ) + + # A child prefix exists but does not reduce the available IP count. + Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/26'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + self.assertEqual(prefix.get_available_ip_count(), 254) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_container_vrf_duplicate_hosts(self): + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_CONTAINER, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.1/24'), vrf=vrf1) + IPAddress.objects.create(address=IPNetwork('192.0.2.1/24'), vrf=vrf2) + IPAddress.objects.create(address=IPNetwork('192.0.2.2/24'), vrf=vrf2) + + # A global container counts child IPs from all VRFs; the duplicate host + # counts once. 254 usable - 2 distinct hosts. + self.assertEqual(prefix.get_available_ip_count(), 252) + self.assertAvailableIPCountMatchesIPSet(prefix) + + def test_available_ip_count_container_vrf_ip_in_populated_range(self): + vrf1 = VRF.objects.create(name='VRF 1') + + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_CONTAINER, + ) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + mark_populated=True, + ) + IPAddress.objects.create(address=IPNetwork('192.0.2.15/24'), vrf=vrf1) + + # The range covers 10 hosts; the VRF 1 IP inside it is not counted again. + self.assertEqual(prefix.get_available_ip_count(), 244) + self.assertAvailableIPCountMatchesIPSet(prefix) + def test_get_first_available_prefix(self): prefixes = Prefix.objects.bulk_create(( @@ -368,6 +900,42 @@ class PrefixTestCase(TestCase): parent_prefix = Prefix.objects.create(prefix=IPNetwork('2001:db8:500:5::/127')) self.assertEqual(parent_prefix.get_first_available_ip(), '2001:db8:500:5::/127') + def test_get_first_available_ip_ipv6_zero_address(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('::/126'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # Normal IPv6 prefixes exclude the subnet-router anycast address ::. + self.assertEqual(prefix.get_first_available_ip(), '::1/126') + + def test_get_first_available_ip_populated_ranges(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.1/29')) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.2/29'), + end_address=IPNetwork('192.0.2.3/29'), + mark_populated=True, + ) + + self.assertEqual(prefix.get_first_available_ip(), '192.0.2.4/29') + + def test_get_first_available_ip_full(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/30'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.1/30')) + IPAddress.objects.create(address=IPNetwork('192.0.2.2/30')) + + self.assertIsNone(prefix.get_first_available_ip()) + def test_get_utilization_container(self): prefixes = ( Prefix(prefix=IPNetwork('10.0.0.0/24'), status=PrefixStatusChoices.STATUS_CONTAINER), @@ -397,6 +965,276 @@ class PrefixTestCase(TestCase): ) self.assertEqual(prefix.get_utilization(), 64 / 254 * 100) # ~25% utilization + def test_get_utilization_distinct_hosts(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.10/24')), + IPAddress(address=IPNetwork('192.0.2.10/32')), + IPAddress(address=IPNetwork('192.0.2.11/24')), + )) + + # Two unique occupied hosts over 254 usable IPv4 addresses. + self.assertEqual(prefix.get_utilization(), 2 / 254 * 100) + + @override_settings(ENFORCE_GLOBAL_UNIQUE=False) + def test_get_utilization_duplicate_ips_global(self): + """ + Tests that identical global IPs permitted by disabled uniqueness count as one host. + """ + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.10/24')) + duplicate_ip = IPAddress(address=IPNetwork('192.0.2.10/24')) + self.assertIsNone(duplicate_ip.clean()) + duplicate_ip.save() + + self.assertEqual(prefix.get_utilization(), 1 / 254 * 100) + + def test_get_utilization_duplicate_ips_vrf(self): + """ + Tests that identical IPs in a non-unique VRF count as one host. + """ + vrf = VRF.objects.create(name='VRF 1', enforce_unique=False) + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + vrf=vrf, + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.10/24'), vrf=vrf) + duplicate_ip = IPAddress(address=IPNetwork('192.0.2.10/24'), vrf=vrf) + self.assertIsNone(duplicate_ip.clean()) + duplicate_ip.save() + + self.assertEqual(prefix.get_utilization(), 1 / 254 * 100) + + def test_available_ip_count_duplicate_ips_vrf(self): + """ + Tests that identical IPs in a non-unique VRF reduce availability once. + """ + vrf = VRF.objects.create(name='VRF 1', enforce_unique=False) + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/29'), + vrf=vrf, + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.1/29'), vrf=vrf), + IPAddress(address=IPNetwork('192.0.2.1/29'), vrf=vrf), + )) + + # Usable hosts in /29: 6. The duplicate occupies a single host. + self.assertEqual(prefix.get_available_ip_count(), 5) + self.assertAvailableIPCountMatchesIPSet(prefix) + + @override_settings(ENFORCE_GLOBAL_UNIQUE=False) + def test_get_ip_usage_summary_duplicate_ips_global(self): + """ + Tests that the usage summary deduplicates identical global IPs in both values. + """ + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.10/24')), + IPAddress(address=IPNetwork('192.0.2.10/24')), + )) + + summary = prefix.get_ip_usage_summary() + + self.assertEqual(summary['available_ip_count'], 253) + self.assertEqual(summary['utilization'], 1 / 254 * 100) + + def test_get_utilization_utilized_ranges(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + mark_utilized=True, + ) + + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.1/24')), + IPAddress(address=IPNetwork('192.0.2.10/24')), + IPAddress(address=IPNetwork('192.0.2.11/24')), + IPAddress(address=IPNetwork('192.0.2.20/24')), + )) + + # Utilized range contributes 10 hosts; IPs inside the range are not double-counted. + # Outside IPs: .1 and .20 => 2 more. + self.assertEqual(prefix.get_utilization(), 12 / 254 * 100) + + def test_get_utilization_overlapping_utilized_ranges(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + mark_utilized=True, + ) + IPRange.objects.create( + start_address=IPNetwork('192.0.2.15/24'), + end_address=IPNetwork('192.0.2.24/24'), + mark_utilized=True, + ) + + # Union is .10-.24 => 15 hosts, not 20. + self.assertEqual(prefix.get_utilization(), 15 / 254 * 100) + + def test_get_utilization_fully_utilized_range(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + # Utilized range covers every usable host (.1-.254 in a non-pool /24). + IPRange.objects.create( + start_address=IPNetwork('192.0.2.1/24'), + end_address=IPNetwork('192.0.2.254/24'), + mark_utilized=True, + ) + + # Exercises the early-return path that skips the child-IP count entirely. + self.assertEqual(prefix.get_utilization(), 100) + + def test_get_utilization_ipv6_utilized_range(self): + prefix = Prefix.objects.create( + prefix=IPNetwork('2001:db8::/126'), + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPRange.objects.create( + start_address=IPNetwork('2001:db8::1/126'), + end_address=IPNetwork('2001:db8::2/126'), + mark_utilized=True, + ) + + self.assertEqual(prefix.get_utilization(), 2 / 4 * 100) + + def test_get_utilization_vrf(self): + vrf1 = VRF.objects.create(name='VRF 1') + vrf2 = VRF.objects.create(name='VRF 2') + + prefix = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + vrf=vrf1, + status=PrefixStatusChoices.STATUS_ACTIVE, + ) + + IPAddress.objects.create(address=IPNetwork('192.0.2.1/24'), vrf=vrf1) + IPAddress.objects.create(address=IPNetwork('192.0.2.15/24'), vrf=vrf1) + IPAddress.objects.create(address=IPNetwork('192.0.2.2/24'), vrf=vrf2) + IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + vrf=vrf2, + mark_utilized=True, + ) + + # VRF 2 objects are ignored entirely; the VRF 1 IP at .15 still counts even + # though it falls inside the VRF 2 range's host span (exclusion intervals are + # built only from same-VRF ranges). + self.assertEqual(prefix.get_utilization(), 2 / 254 * 100) + + def test_get_utilization_query_count(self): + """ + Tests that utilization for a non-container prefix uses two queries. + """ + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24')) + + with self.assertNumQueries(2): + prefix.get_utilization() + + def test_get_ip_usage_summary(self): + """ + Tests that the combined summary matches the independent methods. + """ + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24')) + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.1/24')), + IPAddress(address=IPNetwork('192.0.2.2/24')), + )) + IPRange.objects.create( + start_address=IPNetwork('192.0.2.10/24'), + end_address=IPNetwork('192.0.2.19/24'), + mark_utilized=True, + ) + IPRange.objects.create( + start_address=IPNetwork('192.0.2.30/24'), + end_address=IPNetwork('192.0.2.39/24'), + mark_populated=True, + ) + + summary = prefix.get_ip_usage_summary() + + self.assertEqual(summary['available_ip_count'], prefix.get_available_ip_count()) + self.assertEqual(summary['utilization'], prefix.get_utilization()) + + def test_get_ip_usage_summary_query_count(self): + """ + Tests that the combined summary uses a single distinct-host scan (three queries). + """ + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24')) + + with self.assertNumQueries(3): + prefix.get_ip_usage_summary() + + def test_get_ip_usage_summary_container(self): + """ + Tests that the summary delegates to the independent methods for containers. + """ + container = Prefix.objects.create( + prefix=IPNetwork('192.0.2.0/24'), + status=PrefixStatusChoices.STATUS_CONTAINER, + ) + + summary = container.get_ip_usage_summary() + + self.assertEqual(summary['available_ip_count'], container.get_available_ip_count()) + self.assertEqual(summary['utilization'], container.get_utilization()) + + def test_get_ip_usage_summary_mark_utilized(self): + """ + Tests that a marked-utilized prefix reports 100% utilization in the summary. + """ + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24'), mark_utilized=True) + + summary = prefix.get_ip_usage_summary() + + self.assertEqual(summary['utilization'], 100) + self.assertEqual(summary['available_ip_count'], prefix.get_available_ip_count()) + + def test_usable_size(self): + self.assertEqual(Prefix(prefix=IPNetwork('192.0.2.0/24')).usable_size, 254) + self.assertEqual(Prefix(prefix=IPNetwork('192.0.2.0/24'), is_pool=True).usable_size, 256) + self.assertEqual(Prefix(prefix=IPNetwork('2001:db8::/126')).usable_size, 3) + + def test_usable_ip_bounds_string_prefix(self): + """ + Tests that usable bounds are computed for a string-assigned prefix. + """ + first_ip, last_ip = Prefix(prefix='192.0.2.0/24').usable_ip_bounds + + self.assertEqual(first_ip, netaddr.IPAddress('192.0.2.1')) + self.assertEqual(last_ip, netaddr.IPAddress('192.0.2.254')) + # # Uniqueness enforcement tests # @@ -622,6 +1460,35 @@ class PrefixHierarchyTestCase(TestCase): self.assertEqual(prefixes[3]._depth, 2) self.assertEqual(prefixes[3]._children, 0) + def test_rebuild_prefixes_accepts_vrf_identifier(self): + # None means "global table". Wipe the precomputed hierarchy so the rebuild is observable. + Prefix.objects.update(_depth=0, _children=0) + + rebuild_prefixes(None) + + top = Prefix.objects.get(prefix='10.0.0.0/8') + mid = Prefix.objects.get(prefix='10.0.0.0/16') + leaf = Prefix.objects.get(prefix='10.0.0.0/24') + self.assertEqual((top._depth, top._children), (0, 2)) + self.assertEqual((mid._depth, mid._children), (1, 1)) + self.assertEqual((leaf._depth, leaf._children), (2, 0)) + + def test_rebuild_prefixes_accepts_vrf_pk(self): + # A VRF pk filters to that VRF's prefixes. + vrf = VRF.objects.create(name='VRF 1') + Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24'), vrf=vrf) + Prefix.objects.create(prefix=IPNetwork('192.0.2.0/25'), vrf=vrf) + + # Reset depth/children so the rebuild has something to restore. + Prefix.objects.filter(vrf=vrf).update(_depth=0, _children=0) + + rebuild_prefixes(vrf.pk) + + parent = Prefix.objects.get(prefix='192.0.2.0/24', vrf=vrf) + child = Prefix.objects.get(prefix='192.0.2.0/25', vrf=vrf) + self.assertEqual((parent._depth, parent._children), (0, 1)) + self.assertEqual((child._depth, child._children), (1, 0)) + class IPAddressTestCase(TestCase): @@ -717,6 +1584,20 @@ class IPAddressTestCase(TestCase): with self.assertRaisesMessage(ValidationError, 'Cannot create IP address'): ipaddress.clean() + def test_populated_range_blocks_ip_with_different_mask(self): + # The populated-range check compares by host portion, so a different mask + # must not let an IPAddress slip past validation. + IPRange.objects.create( + start_address=IPNetwork('10.0.0.2/24'), + end_address=IPNetwork('10.0.0.254/24'), + mark_populated=True, + ) + + ip = IPAddress(address=IPNetwork('10.0.0.2/32')) + + with self.assertRaises(ValidationError): + ip.full_clean() + class VLANGroupTestCase(TestCase): @@ -926,3 +1807,119 @@ class VLANTestCase(TestCase): vlan.group = vlangroups[2] with self.assertRaises(ValidationError): vlan.full_clean() + + def test_vlan_group_vid_validation_with_null_vid(self): + """A missing VID on a grouped VLAN raises a ValidationError, not a TypeError.""" + group = VLANGroup.objects.create(name='VLAN Group 1', slug='vlan-group-1') + vlan = VLAN(name='VLAN X', vid=None, group=group) + with self.assertRaises(ValidationError): + vlan.full_clean() + + +class PrefixGetChildIPsTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.prefix = Prefix.objects.create(prefix='10.0.0.0/24') + IPAddress.objects.bulk_create(( + IPAddress(address='10.0.0.0/24'), # Network address (inside containment) + IPAddress(address='10.0.0.1/24'), + IPAddress(address='10.0.0.255/24'), # Broadcast address (inside containment) + IPAddress(address='10.0.1.1/24'), # Outside the prefix + )) + + def test_get_child_ips_matches_net_host_contained(self): + """get_child_ips returns the same IPs as the net_host_contained containment lookup.""" + expected = set( + IPAddress.objects.filter( + address__net_host_contained=str(self.prefix.prefix), vrf=None + ).values_list('pk', flat=True) + ) + actual = set(self.prefix.get_child_ips().values_list('pk', flat=True)) + self.assertEqual(actual, expected) + self.assertEqual(len(actual), 3) + + def test_get_child_ips_sql_avoids_containment_recheck(self): + """get_child_ips filters on an inet host range, not the <<= containment operator.""" + sql = str(self.prefix.get_child_ips().query) + self.assertNotIn('<<=', sql) + + def test_get_child_ips_container_in_global_table_spans_vrfs(self): + """A container prefix in the global table returns child IPs from any VRF.""" + vrf = VRF.objects.create(name='VRF 1') + container = Prefix.objects.create( + prefix='10.1.0.0/24', status=PrefixStatusChoices.STATUS_CONTAINER, + ) + in_vrf = IPAddress.objects.create(address='10.1.0.5/24', vrf=vrf) + in_global = IPAddress.objects.create(address='10.1.0.6/24') + child_pks = set(container.get_child_ips().values_list('pk', flat=True)) + self.assertEqual(child_pks, {in_vrf.pk, in_global.pk}) + + +class ServiceTemplateTestCase(TestCase): + + def test_servicetemplate_lowest_port(self): + """ + Test lowest port setting for servicetemplate + """ + template = ServiceTemplate( + name='Template 1', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=[80, 443, 22, 8080], # small test list + ) + template.full_clean() + template.save() + self.assertEqual(template._ports_lowest, 22) + + def test_servicetemplate_single_port(self): + """ + Test with a single port + """ + template = ServiceTemplate( + name='Template 2', + protocol=ServiceProtocolChoices.PROTOCOL_UDP, + ports=[53], + ) + template.full_clean() + template.save() + self.assertEqual(template._ports_lowest, 53) + + def test_servicetemplate_empty_ports(self): + """ + Test with empty ports list + """ + template = ServiceTemplate( + name='Template 3', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=[], + ) + self.assertRaises(ValidationError, template.full_clean) + + +class ServiceTestCase(TestCase): + + @classmethod + def setUpTestData(cls): + site = Site.objects.create( + name='Site 1', + slug='site-1', + ) + VirtualMachine.objects.create( + name='virtual machine 1', + site=site, + ) + + def test_large_service(self): + """ + Test creation of service with large number of ports. + Related to issue #22273 + """ + service = Service( + name='Service 1', + protocol=ServiceProtocolChoices.PROTOCOL_TCP, + ports=list(range(SERVICE_PORT_MIN, SERVICE_PORT_MAX)), + parent=VirtualMachine.objects.first(), + ) + service.full_clean() + # Testing .save() is the important part, to check for database problems + service.save() + self.assertEqual(service._ports_lowest, SERVICE_PORT_MIN) diff --git a/netbox/ipam/tests/test_querysets.py b/netbox/ipam/tests/test_querysets.py new file mode 100644 index 000000000..feb8ff2cd --- /dev/null +++ b/netbox/ipam/tests/test_querysets.py @@ -0,0 +1,344 @@ +import netaddr +from django.test import TestCase +from netaddr import IPNetwork + +from ipam.models import IPAddress, IPRange + + +class IPAddressQuerySetTestCase(TestCase): + @classmethod + def setUpTestData(cls): + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.1/24')), + IPAddress(address=IPNetwork('192.0.2.1/32')), + IPAddress(address=IPNetwork('192.0.2.2/24')), + )) + + def test_count_distinct_hosts(self): + """ + Tests that duplicate hosts with different masks are counted once. + """ + self.assertEqual(IPAddress.objects.count_distinct_hosts(), 2) + + def test_count_distinct_hosts_empty(self): + """ + Tests that an empty queryset counts zero hosts. + """ + self.assertEqual(IPAddress.objects.none().count_distinct_hosts(), 0) + + def test_count_distinct_hosts_exclude_intervals(self): + """ + Tests that hosts covered by an excluded interval are not counted. + """ + interval = (netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.1')) + self.assertEqual(IPAddress.objects.count_distinct_hosts(exclude_intervals=[interval]), 1) + + def test_count_distinct_hosts_pair(self): + """ + Tests that the bounded and total distinct host counts are computed correctly. + """ + counts = IPAddress.objects.count_distinct_hosts_pair( + bounds=(netaddr.IPAddress('192.0.2.2'), netaddr.IPAddress('192.0.2.10')), + bounded_exclude=[(netaddr.IPAddress('192.0.2.2'), netaddr.IPAddress('192.0.2.2'))], + total_exclude=[(netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.1'))], + ) + self.assertEqual(counts, {'bounded': 0, 'total': 1}) + + def test_count_distinct_hosts_pair_no_excludes(self): + """ + Tests that both counts dedupe hosts and respect the bounds without excludes. + """ + counts = IPAddress.objects.count_distinct_hosts_pair( + bounds=(netaddr.IPAddress('192.0.2.2'), netaddr.IPAddress('192.0.2.10')), + ) + self.assertEqual(counts, {'bounded': 1, 'total': 2}) + + def test_first_available_host(self): + """ + Tests that occupied hosts and excluded intervals are skipped, including hosts behind the sweep. + """ + interval = (netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.5')) + self.assertEqual( + IPAddress.objects.first_available_host( + netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10'), exclude_intervals=[interval] + ), + netaddr.IPAddress('192.0.2.6'), + ) + + def test_first_available_host_inverted_bounds(self): + """ + Tests that an inverted bounds pair yields None. + """ + self.assertIsNone( + IPAddress.objects.first_available_host(netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.5')) + ) + + def test_available_intervals(self): + """ + Tests that gaps around occupied hosts and excluded intervals are yielded in order. + """ + interval = (netaddr.IPAddress('192.0.2.5'), netaddr.IPAddress('192.0.2.6')) + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10'), exclude_intervals=[interval] + )), + [ + (netaddr.IPAddress('192.0.2.3'), netaddr.IPAddress('192.0.2.4')), + (netaddr.IPAddress('192.0.2.7'), netaddr.IPAddress('192.0.2.10')), + ], + ) + + def test_available_intervals_leading_gap(self): + """ + Tests that the gap before the first occupied host is yielded. + """ + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.0'), netaddr.IPAddress('192.0.2.2') + )), + [(netaddr.IPAddress('192.0.2.0'), netaddr.IPAddress('192.0.2.0'))], + ) + + def test_available_intervals_empty_queryset(self): + """ + Tests that an empty queryset yields the full span. + """ + self.assertEqual( + list(IPAddress.objects.none().available_intervals( + netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.3') + )), + [(netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.3'))], + ) + + def test_available_intervals_inverted_bounds(self): + """ + Tests that an inverted bounds pair yields nothing. + """ + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.5') + )), + [], + ) + + def test_available_intervals_fully_excluded(self): + """ + Tests that a span covered by an excluded interval yields nothing. + """ + interval = (netaddr.IPAddress('192.0.2.0'), netaddr.IPAddress('192.0.2.20')) + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10'), exclude_intervals=[interval] + )), + [], + ) + + def test_available_intervals_mixed_family_exclude(self): + """ + Tests that an exclude interval spanning address families is ignored. + """ + interval = (netaddr.IPAddress('192.0.2.5'), netaddr.IPAddress('2001:db8::5')) + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10'), exclude_intervals=[interval] + )), + [(netaddr.IPAddress('192.0.2.3'), netaddr.IPAddress('192.0.2.10'))], + ) + + def test_available_intervals_invalid_batch_size(self): + """ + Tests that a non-positive batch size raises ValueError. + """ + intervals = IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10'), batch_size=0 + ) + with self.assertRaises(ValueError): + next(intervals) + + def test_available_intervals_first_interval_single_query(self): + """ + Tests that consuming only the first interval issues a single batch query. + """ + IPAddress.objects.bulk_create(( + IPAddress(address=IPNetwork('192.0.2.12/24')), + IPAddress(address=IPNetwork('192.0.2.14/24')), + IPAddress(address=IPNetwork('192.0.2.16/24')), + )) + + intervals = IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.20'), batch_size=1 + ) + + with self.assertNumQueries(1): + self.assertEqual( + next(intervals), + (netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.11')), + ) + + def test_available_intervals_unsorted_exclude_intervals(self): + """ + Tests that unsorted, overlapping exclude intervals are normalized internally. + """ + intervals = list(IPAddress.objects.none().available_intervals( + netaddr.IPAddress('192.0.2.1'), + netaddr.IPAddress('192.0.2.40'), + exclude_intervals=[ + (netaddr.IPAddress('192.0.2.20'), netaddr.IPAddress('192.0.2.30')), + (netaddr.IPAddress('192.0.2.1'), netaddr.IPAddress('192.0.2.10')), + (netaddr.IPAddress('192.0.2.25'), netaddr.IPAddress('192.0.2.30')), + ], + )) + + self.assertEqual(intervals, [ + (netaddr.IPAddress('192.0.2.11'), netaddr.IPAddress('192.0.2.19')), + (netaddr.IPAddress('192.0.2.31'), netaddr.IPAddress('192.0.2.40')), + ]) + + def test_available_intervals_batching(self): + """ + Tests that gaps spanning multiple fetch batches are yielded completely and in order. + """ + IPAddress.objects.bulk_create( + IPAddress(address=IPNetwork(f'192.0.3.{i}/24')) for i in range(2, 82, 2) + ) + expected = [ + (netaddr.IPAddress(f'192.0.3.{i}'), netaddr.IPAddress(f'192.0.3.{i}')) + for i in range(1, 83, 2) + ] + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.3.1'), netaddr.IPAddress('192.0.3.81'), batch_size=8 + )), + expected, + ) + + def test_iter_distinct_hosts_stops_at_upper_bound(self): + """ + Tests that batch resumption stops once the last fetched host reaches the upper bound. + """ + IPAddress.objects.bulk_create( + IPAddress(address=IPNetwork(f'192.0.4.{i}/24')) for i in (2, 4) + ) + self.assertEqual( + list(IPAddress.objects.all()._iter_distinct_hosts( + netaddr.IPAddress('192.0.4.2'), netaddr.IPAddress('192.0.4.4'), batch_size=1 + )), + [netaddr.IPAddress('192.0.4.2'), netaddr.IPAddress('192.0.4.4')], + ) + + def test_available_intervals_batch_size_one(self): + """ + Tests that fetching one host per batch still terminates and yields every gap. + """ + IPAddress.objects.bulk_create( + IPAddress(address=IPNetwork(f'192.0.3.{i}/24')) for i in (2, 3, 5) + ) + self.assertEqual( + list(IPAddress.objects.available_intervals( + netaddr.IPAddress('192.0.3.1'), netaddr.IPAddress('192.0.3.6'), batch_size=1 + )), + [ + (netaddr.IPAddress('192.0.3.1'), netaddr.IPAddress('192.0.3.1')), + (netaddr.IPAddress('192.0.3.4'), netaddr.IPAddress('192.0.3.4')), + (netaddr.IPAddress('192.0.3.6'), netaddr.IPAddress('192.0.3.6')), + ], + ) + + +class IPRangeQuerySetTestCase(TestCase): + @classmethod + def setUpTestData(cls): + IPRange.objects.bulk_create(( + IPRange(start_address=IPNetwork('192.0.2.10/24'), end_address=IPNetwork('192.0.2.19/24'), size=10), + IPRange(start_address=IPNetwork('192.0.2.15/24'), end_address=IPNetwork('192.0.2.24/24'), size=10), + IPRange(start_address=IPNetwork('192.0.2.40/24'), end_address=IPNetwork('192.0.2.49/24'), size=10), + )) + + def test_get_intervals_merges_overlaps(self): + """ + Tests that overlapping ranges merge and disjoint ranges stay separate. + """ + self.assertEqual( + IPRange.objects.get_intervals(), + [ + (netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.24')), + (netaddr.IPAddress('192.0.2.40'), netaddr.IPAddress('192.0.2.49')), + ], + ) + + def test_get_intervals_clips_to_bounds(self): + """ + Tests that ranges are clipped to the bounds and out-of-bounds ranges are dropped. + """ + self.assertEqual( + IPRange.objects.get_intervals(netaddr.IPAddress('192.0.2.20'), netaddr.IPAddress('192.0.2.30')), + [(netaddr.IPAddress('192.0.2.20'), netaddr.IPAddress('192.0.2.24'))], + ) + + def test_get_intervals_drops_ranges_below_bounds(self): + """ + Tests that ranges entirely below the lower bound are dropped. + """ + self.assertEqual( + IPRange.objects.get_intervals(netaddr.IPAddress('192.0.2.30'), netaddr.IPAddress('192.0.2.60')), + [(netaddr.IPAddress('192.0.2.40'), netaddr.IPAddress('192.0.2.49'))], + ) + + def test_get_intervals_drops_ranges_above_bounds(self): + """ + Tests that ranges entirely above the upper bound are dropped. + """ + self.assertEqual( + IPRange.objects.get_intervals(netaddr.IPAddress('192.0.2.0'), netaddr.IPAddress('192.0.2.30')), + [(netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.24'))], + ) + + def test_get_intervals_clips_to_upper_bound(self): + """ + Tests that a range straddling the upper bound is clipped to it. + """ + self.assertEqual( + IPRange.objects.get_intervals(netaddr.IPAddress('192.0.2.0'), netaddr.IPAddress('192.0.2.15')), + [(netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.15'))], + ) + + def test_get_intervals_mixed_families(self): + """ + Tests that int-adjacent intervals of different address families are not merged. + """ + IPRange.objects.bulk_create(( + IPRange( + start_address=IPNetwork('255.255.255.254/32'), + end_address=IPNetwork('255.255.255.255/32'), + size=2, + ), + IPRange(start_address=IPNetwork('::1/128'), end_address=IPNetwork('::2/128'), size=2), + )) + + self.assertEqual( + IPRange.objects.get_intervals(), + [ + (netaddr.IPAddress('192.0.2.10'), netaddr.IPAddress('192.0.2.24')), + (netaddr.IPAddress('192.0.2.40'), netaddr.IPAddress('192.0.2.49')), + (netaddr.IPAddress('255.255.255.254'), netaddr.IPAddress('255.255.255.255')), + (netaddr.IPAddress('::1'), netaddr.IPAddress('::2')), + ], + ) + + def test_get_intervals_ipv6(self): + """ + Tests that IPv6 ranges merge and clip by host address. + """ + IPRange.objects.create( + start_address=IPNetwork('2001:db8::10/64'), + end_address=IPNetwork('2001:db8::1f/64'), + ) + IPRange.objects.create( + start_address=IPNetwork('2001:db8::18/64'), + end_address=IPNetwork('2001:db8::2f/64'), + ) + + self.assertEqual( + IPRange.objects.get_intervals(netaddr.IPAddress('2001:db8::'), netaddr.IPAddress('2001:db8::ffff')), + [(netaddr.IPAddress('2001:db8::10'), netaddr.IPAddress('2001:db8::2f'))], + ) diff --git a/netbox/ipam/tests/test_views.py b/netbox/ipam/tests/test_views.py index 10932ce72..9bf73038f 100644 --- a/netbox/ipam/tests/test_views.py +++ b/netbox/ipam/tests/test_views.py @@ -1,6 +1,8 @@ import datetime from django.contrib.contenttypes.models import ContentType +from django.db.backends.postgresql.psycopg_any import NumericRange +from django.test import RequestFactory from django.urls import reverse from netaddr import IPNetwork @@ -8,8 +10,10 @@ from core.choices import ObjectChangeActionChoices from core.models import ObjectChange, ObjectType from dcim.constants import InterfaceTypeChoices from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Site +from extras.models import SavedFilter from ipam.choices import * from ipam.models import * +from ipam.views import AggregatePrefixesView from netbox.choices import CSVDelimiterChoices, ImportFormatChoices from tenancy.models import Tenant from users.models import ObjectPermission @@ -353,6 +357,101 @@ class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase): url = reverse('ipam:aggregate_prefixes', kwargs={'pk': aggregate.pk}) self.assertHttpStatus(self.client.get(url), 200) + def test_aggregate_prefixes_filter_suppresses_available_prefixes(self): + self.add_permissions('ipam.view_aggregate', 'ipam.view_prefix') + + tenants = ( + Tenant(name='Aggregate Tenant 1', slug='aggregate-tenant-1'), + Tenant(name='Aggregate Tenant 2', slug='aggregate-tenant-2'), + ) + Tenant.objects.bulk_create(tenants) + + aggregate = Aggregate.objects.create( + prefix=IPNetwork('203.0.113.0/24'), + rir=RIR.objects.first() + ) + prefixes = ( + Prefix(prefix=IPNetwork('203.0.113.0/26'), tenant=tenants[0]), + Prefix(prefix=IPNetwork('203.0.113.64/26'), tenant=tenants[1]), + ) + Prefix.objects.bulk_create(prefixes) + + url = reverse('ipam:aggregate_prefixes', kwargs={'pk': aggregate.pk}) + response = self.client.get(url, {'tenant_id': tenants[0].pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, '203.0.113.0/26') + self.assertNotContains(response, '203.0.113.64/26') + + def test_aggregate_prefixes_saved_filter(self): + self.add_permissions('ipam.view_aggregate', 'ipam.view_prefix') + + tenants = ( + Tenant(name='Aggregate Saved Tenant 1', slug='aggregate-saved-tenant-1'), + Tenant(name='Aggregate Saved Tenant 2', slug='aggregate-saved-tenant-2'), + ) + Tenant.objects.bulk_create(tenants) + + aggregate = Aggregate.objects.create( + prefix=IPNetwork('203.0.114.0/24'), + rir=RIR.objects.first() + ) + prefixes = ( + Prefix(prefix=IPNetwork('203.0.114.0/26'), tenant=tenants[0]), + Prefix(prefix=IPNetwork('203.0.114.64/26'), tenant=tenants[1]), + ) + Prefix.objects.bulk_create(prefixes) + + saved_filter = SavedFilter.objects.create( + name='Aggregate Tenant 1 prefixes', + slug='aggregate-tenant-1-prefixes', + parameters={ + 'tenant_id': [str(tenants[0].pk)], + }, + ) + saved_filter.object_types.add(ObjectType.objects.get_for_model(Prefix)) + + url = reverse('ipam:aggregate_prefixes', kwargs={'pk': aggregate.pk}) + response = self.client.get(url, {'filter_id': saved_filter.pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, '203.0.114.0/26') + self.assertNotContains(response, '203.0.114.64/26') + + def test_children_are_filtered_fallback(self): + """_children_are_filtered() rebuilds the queryset when prep_table_data() has not cached a result.""" + self.add_permissions('ipam.view_aggregate', 'ipam.view_prefix') + + aggregate = Aggregate.objects.create( + prefix=IPNetwork('203.0.115.0/24'), + rir=RIR.objects.first() + ) + tenant = Tenant.objects.create(name='Aggregate Fallback Tenant', slug='aggregate-fallback-tenant') + Prefix.objects.create(prefix=IPNetwork('203.0.115.0/26'), tenant=tenant) + Prefix.objects.create(prefix=IPNetwork('203.0.115.64/26')) + + # No cached value: the fallback path rebuilds the filtered queryset and detects the filter. + view = AggregatePrefixesView() + request = RequestFactory().get('/', {'tenant_id': tenant.pk}) + request.user = self.user + self.assertFalse(hasattr(view, '_child_queryset_is_filtered')) + self.assertTrue(view._children_are_filtered(request, aggregate)) + + # No cached value and no filter: the fallback path reports no filtering. + view = AggregatePrefixesView() + request = RequestFactory().get('/') + request.user = self.user + self.assertFalse(view._children_are_filtered(request, aggregate)) + + # A cached value takes precedence over the actual request state. + view = AggregatePrefixesView() + view._set_children_filtered(False) + request = RequestFactory().get('/', {'tenant_id': tenant.pk}) + request.user = self.user + self.assertFalse(view._children_are_filtered(request, aggregate)) + class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): model = Role @@ -588,6 +687,63 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): url = reverse('ipam:prefix_prefixes', kwargs={'pk': prefixes[0].pk}) self.assertHttpStatus(self.client.get(url), 200) + def test_prefix_prefixes_filter_suppresses_available_prefixes(self): + self.add_permissions('ipam.view_prefix') + + tenants = ( + Tenant(name='Prefix Tenant 1', slug='prefix-tenant-1'), + Tenant(name='Prefix Tenant 2', slug='prefix-tenant-2'), + ) + Tenant.objects.bulk_create(tenants) + + parent = Prefix.objects.create(prefix=IPNetwork('198.51.100.0/24')) + prefixes = ( + Prefix(prefix=IPNetwork('198.51.100.0/26'), tenant=tenants[0]), + Prefix(prefix=IPNetwork('198.51.100.64/26'), tenant=tenants[1]), + ) + Prefix.objects.bulk_create(prefixes) + + url = reverse('ipam:prefix_prefixes', kwargs={'pk': parent.pk}) + response = self.client.get(url, {'tenant_id': tenants[0].pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, '198.51.100.0/26') + self.assertNotContains(response, '198.51.100.64/26') + + def test_prefix_prefixes_saved_filter_suppresses_available_prefixes(self): + self.add_permissions('ipam.view_prefix') + + tenants = ( + Tenant(name='Prefix Saved Tenant 1', slug='prefix-saved-tenant-1'), + Tenant(name='Prefix Saved Tenant 2', slug='prefix-saved-tenant-2'), + ) + Tenant.objects.bulk_create(tenants) + + parent = Prefix.objects.create(prefix=IPNetwork('198.51.101.0/24')) + prefixes = ( + Prefix(prefix=IPNetwork('198.51.101.0/26'), tenant=tenants[0]), + Prefix(prefix=IPNetwork('198.51.101.64/26'), tenant=tenants[1]), + ) + Prefix.objects.bulk_create(prefixes) + + saved_filter = SavedFilter.objects.create( + name='Prefix Tenant 1 prefixes', + slug='prefix-tenant-1-prefixes', + parameters={ + 'tenant_id': [str(tenants[0].pk)], + }, + ) + saved_filter.object_types.add(ObjectType.objects.get_for_model(Prefix)) + + url = reverse('ipam:prefix_prefixes', kwargs={'pk': parent.pk}) + response = self.client.get(url, {'filter_id': saved_filter.pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, '198.51.101.0/26') + self.assertNotContains(response, '198.51.101.64/26') + def test_prefix_ipranges(self): self.add_permissions('ipam.view_prefix', 'ipam.view_iprange') prefix = Prefix.objects.create(prefix=IPNetwork('192.168.0.0/16')) @@ -616,6 +772,89 @@ class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase): url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk}) self.assertHttpStatus(self.client.get(url), 200) + def test_prefix_ipaddresses_filter(self): + self.add_permissions('ipam.view_prefix', 'ipam.view_ipaddress', 'ipam.view_iprange') + + tenants = ( + Tenant(name='IP Address Tenant 1', slug='ip-address-tenant-1'), + Tenant(name='IP Address Tenant 2', slug='ip-address-tenant-2'), + ) + Tenant.objects.bulk_create(tenants) + + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24')) + ip_addresses = ( + IPAddress(address=IPNetwork('192.0.2.1/24'), tenant=tenants[0]), + IPAddress(address=IPNetwork('192.0.2.2/24'), tenant=tenants[1]), + ) + IPAddress.objects.bulk_create(ip_addresses) + + url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk}) + response = self.client.get(url, {'tenant_id': tenants[0].pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, '192.0.2.1/24') + self.assertNotContains(response, '192.0.2.2/24') + + def test_prefix_ipaddresses_saved_filter(self): + self.add_permissions('ipam.view_prefix', 'ipam.view_ipaddress', 'ipam.view_iprange') + + tenants = ( + Tenant(name='Saved Filter Tenant 1', slug='saved-filter-tenant-1'), + Tenant(name='Saved Filter Tenant 2', slug='saved-filter-tenant-2'), + ) + Tenant.objects.bulk_create(tenants) + + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/24')) + ip_addresses = ( + IPAddress(address=IPNetwork('192.0.2.1/24'), tenant=tenants[0]), + IPAddress(address=IPNetwork('192.0.2.2/24'), tenant=tenants[1]), + ) + IPAddress.objects.bulk_create(ip_addresses) + + saved_filter = SavedFilter.objects.create( + name='Tenant 1 IP addresses', + slug='tenant-1-ip-addresses', + parameters={ + 'tenant_id': [str(tenants[0].pk)], + }, + ) + saved_filter.object_types.add(ObjectType.objects.get_for_model(IPAddress)) + + url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk}) + response = self.client.get(url, {'filter_id': saved_filter.pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, '192.0.2.1/24') + self.assertNotContains(response, '192.0.2.2/24') + + def test_prefix_ipaddresses_unfiltered_shows_available_space(self): + """An unfiltered IP Addresses tab injects synthetic available-space rows.""" + self.add_permissions('ipam.view_prefix', 'ipam.view_ipaddress', 'ipam.view_iprange') + + prefix = Prefix.objects.create(prefix=IPNetwork('192.0.2.0/29')) + IPAddress.objects.create(address=IPNetwork('192.0.2.1/29')) + + url = reverse('ipam:prefix_ipaddresses', kwargs={'pk': prefix.pk}) + response = self.client.get(url) + + self.assertHttpStatus(response, 200) + self.assertGreater(len(response.context['table'].data), 1) + + def test_prefix_prefixes_unfiltered_shows_available_prefixes(self): + """An unfiltered Child Prefixes tab injects synthetic available-prefix rows.""" + self.add_permissions('ipam.view_prefix') + + parent = Prefix.objects.create(prefix=IPNetwork('198.51.102.0/24')) + Prefix.objects.create(prefix=IPNetwork('198.51.102.0/26')) + + url = reverse('ipam:prefix_prefixes', kwargs={'pk': parent.pk}) + response = self.client.get(url) + + self.assertHttpStatus(response, 200) + self.assertGreater(len(response.context['table'].data), 1) + def test_prefix_ipaddresses_with_single_address_range(self): self.add_permissions('ipam.view_prefix', 'ipam.view_ipaddress', 'ipam.view_iprange') # The IP Addresses tab annotates child IP addresses alongside any @@ -1130,6 +1369,70 @@ class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'description': 'New description', } + def test_vlans_filter_suppresses_available_vlans(self): + self.add_permissions('ipam.view_vlangroup', 'ipam.view_vlan') + + group = VLANGroup.objects.create( + name='Filtered VLAN Group', + slug='filtered-vlan-group' + ) + vlans = ( + VLAN(group=group, vid=100, name='VLAN100'), + VLAN(group=group, vid=200, name='VLAN200'), + ) + VLAN.objects.bulk_create(vlans) + + url = reverse('ipam:vlangroup_vlans', kwargs={'pk': group.pk}) + response = self.client.get(url, {'vid': 100}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, 'VLAN100') + self.assertNotContains(response, 'VLAN200') + + def test_vlans_saved_filter_suppresses_available_vlans(self): + self.add_permissions('ipam.view_vlangroup', 'ipam.view_vlan') + + group = VLANGroup.objects.create( + name='Saved Filter VLAN Group', + slug='saved-filter-vlan-group' + ) + vlans = ( + VLAN(group=group, vid=100, name='VLAN100'), + VLAN(group=group, vid=200, name='VLAN200'), + ) + VLAN.objects.bulk_create(vlans) + + saved_filter = SavedFilter.objects.create( + name='VLAN 100', + slug='vlan-100', + parameters={ + 'vid': ['100'], + }, + ) + saved_filter.object_types.add(ObjectType.objects.get_for_model(VLAN)) + + url = reverse('ipam:vlangroup_vlans', kwargs={'pk': group.pk}) + response = self.client.get(url, {'filter_id': saved_filter.pk}) + + self.assertHttpStatus(response, 200) + self.assertEqual(len(response.context['table'].data), 1) + self.assertContains(response, 'VLAN100') + self.assertNotContains(response, 'VLAN200') + + def test_vlans_unfiltered_shows_available_vlans(self): + """An unfiltered VLANs tab injects synthetic available-VLAN rows.""" + self.add_permissions('ipam.view_vlangroup', 'ipam.view_vlan') + + group = VLANGroup.objects.create(name='Unfiltered VLAN Group', slug='unfiltered-vlan-group') + VLAN.objects.create(group=group, vid=1, name='VLAN0001') + + url = reverse('ipam:vlangroup_vlans', kwargs={'pk': group.pk}) + response = self.client.get(url) + + self.assertHttpStatus(response, 200) + self.assertGreater(len(response.context['table'].data), 1) + class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = VLAN @@ -1199,6 +1502,178 @@ class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'description': 'New description', } + def test_bulk_add_vlans(self): + self.add_permissions('ipam.add_vlan') + + group = VLANGroup.objects.get(name='VLAN Group 1') + initial_count = VLAN.objects.count() + expected_vids = (110, 120, 121, 122) + + form_data = { + 'pattern': '110,120-122', + 'group': group.pk, + 'name': 'Pool-{vid}', + 'status': VLANStatusChoices.STATUS_RESERVED, + } + + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + + self.assertHttpStatus(response, 302) + self.assertEqual(VLAN.objects.count(), initial_count + len(expected_vids)) + + for vid in expected_vids: + self.assertTrue( + VLAN.objects.filter( + group=group, + vid=vid, + name=f'Pool-{vid}' + ).exists() + ) + + def test_bulk_add_vlans_rolls_back_on_duplicate_name(self): + self.add_permissions('ipam.add_vlan') + + group = VLANGroup.objects.get(name='VLAN Group 1') + initial_count = VLAN.objects.count() + + form_data = { + 'pattern': '110-112', + 'group': group.pk, + 'name': 'Duplicate name', + 'status': VLANStatusChoices.STATUS_RESERVED, + } + + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + + self.assertHttpStatus(response, 200) + self.assertEqual(VLAN.objects.count(), initial_count) + self.assertFalse(VLAN.objects.filter(group=group, vid=110).exists()) + + def test_bulk_add_vlans_rolls_back_when_any_id_outside_group_range(self): + self.add_permissions('ipam.add_vlan') + + group = VLANGroup.objects.create( + name='Restricted VLAN Group', + slug='restricted-vlan-group', + vid_ranges=[NumericRange(200, 204)] # Valid VIDs: 200-203 + ) + initial_count = VLAN.objects.count() + + form_data = { + 'pattern': '200-203,500', + 'group': group.pk, + 'name': 'Restricted-{vid}', + 'status': VLANStatusChoices.STATUS_RESERVED, + } + + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + + self.assertHttpStatus(response, 200) + self.assertEqual(VLAN.objects.count(), initial_count) + self.assertFalse(VLAN.objects.filter(group=group, vid=200).exists()) + self.assertFalse(VLAN.objects.filter(group=group, vid=203).exists()) + self.assertFalse(VLAN.objects.filter(group=group, vid=500).exists()) + + def test_bulk_add_vlans_pattern_shapes(self): + """Single values, multiple values, ranges, and combinations create the expected VLANs.""" + self.add_permissions('ipam.add_vlan') + # The combination runs against a second group: subTests share one transaction, and VIDs + # 10 & 20 would otherwise collide with the multiple-values case via the (group, vid) constraint. + cases = ( + ('500', (500,), 'VLAN Group 1'), + ('5,10,20', (5, 10, 20), 'VLAN Group 1'), + ('600-605', tuple(range(600, 606)), 'VLAN Group 1'), + ('1,10-20,300-305', (1, *range(10, 21), *range(300, 306)), 'VLAN Group 2'), + ) + for pattern, expected_vids, group_name in cases: + with self.subTest(pattern=pattern): + group = VLANGroup.objects.get(name=group_name) + initial_count = VLAN.objects.count() + form_data = { + 'pattern': pattern, + 'group': group.pk, + 'name': 'Pool-{vid}', + 'status': VLANStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + self.assertHttpStatus(response, 302) + self.assertEqual(VLAN.objects.count(), initial_count + len(expected_vids)) + for vid in expected_vids: + self.assertTrue(VLAN.objects.filter(group=group, vid=vid, name=f'Pool-{vid}').exists()) + + def test_bulk_add_vlans_invalid_pattern(self): + """An invalid pattern re-renders the form with a pattern error and creates nothing.""" + self.add_permissions('ipam.add_vlan') + initial_count = VLAN.objects.count() + + for pattern in ('abc', '20-10', '0', '4095', '10-'): + with self.subTest(pattern=pattern): + form_data = { + 'pattern': pattern, + 'name': 'Pool-{vid}', + 'status': VLANStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + self.assertHttpStatus(response, 200) + self.assertIn('pattern', response.context['form'].errors) + self.assertEqual(VLAN.objects.count(), initial_count) + + def test_bulk_add_vlans_static_name_without_group(self): + """A static name (no {vid} placeholder) is permitted across VLANs not assigned to a group.""" + self.add_permissions('ipam.add_vlan') + initial_count = VLAN.objects.count() + + form_data = { + 'pattern': '710-712', + 'name': 'Same name', + 'status': VLANStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + + self.assertHttpStatus(response, 302) + self.assertEqual(VLAN.objects.count(), initial_count + 3) + self.assertEqual(VLAN.objects.filter(name='Same name').count(), 3) + + def test_bulk_add_vlans_rolls_back_on_constrained_permission(self): + """Bulk creation rolls back when a generated VLAN falls outside the user's add constraints.""" + obj_perm = ObjectPermission( + name='Test permission', + actions=['add'], + constraints={'vid__lt': 120} + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(VLAN)) + + initial_count = VLAN.objects.count() + form_data = { + 'pattern': '110,120-122', + 'name': 'Pool-{vid}', + 'status': VLANStatusChoices.STATUS_ACTIVE, + } + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + + self.assertHttpStatus(response, 200) + self.assertEqual(VLAN.objects.count(), initial_count) + self.assertTrue(response.context['form'].non_field_errors()) + + def test_bulk_add_vlans_propagates_field_errors(self): + """A per-object validation error on a non-pattern field is reported on the bulk-create form.""" + self.add_permissions('ipam.add_vlan') + initial_count = VLAN.objects.count() + + form_data = { + 'pattern': '800', + 'name': 'Pool-{vid}', + 'status': VLANStatusChoices.STATUS_ACTIVE, + 'qinq_role': VLANQinQRoleChoices.ROLE_CUSTOMER, # Requires an SVLAN + } + response = self.client.post(reverse('ipam:vlan_bulk_add'), form_data) + + self.assertHttpStatus(response, 200) + self.assertEqual(VLAN.objects.count(), initial_count) + self.assertTrue(response.context['form'].non_field_errors()) + class VLANTranslationPolicyTestCase(ViewTestCases.PrimaryObjectViewTestCase): model = VLANTranslationPolicy diff --git a/netbox/ipam/utils.py b/netbox/ipam/utils.py index 390215873..5b29688fd 100644 --- a/netbox/ipam/utils.py +++ b/netbox/ipam/utils.py @@ -1,10 +1,10 @@ from dataclasses import dataclass import netaddr +from django.apps import apps from django.utils.translation import gettext_lazy as _ from .constants import * -from .models import VLAN, Prefix __all__ = ( 'AvailableIPSpace', @@ -39,7 +39,7 @@ def add_requested_prefixes(parent, prefix_list, show_available=True, show_assign requested, create fake Prefix objects for all unallocated space within a prefix. :param parent: Parent Prefix instance - :param prefix_list: Child prefixes list + :param prefix_list: Child prefixes list (or queryset) :param show_available: Include available prefixes. :param show_assigned: Show assigned prefixes. """ @@ -47,6 +47,7 @@ def add_requested_prefixes(parent, prefix_list, show_available=True, show_assign # Add available prefixes to the table if requested if prefix_list and show_available: + Prefix = apps.get_model('ipam', 'Prefix') # Find all unallocated space, add fake Prefix objects to child_prefixes. # IMPORTANT: These are unsaved Prefix instances (pk=None). If this is ever changed to use @@ -78,22 +79,7 @@ def annotate_ip_space(prefix): records = sorted(records, key=lambda x: x[0]) # Determine the first & last valid IP addresses in the prefix - if ( - prefix.is_pool - or (prefix.family == 4 and prefix.mask_length >= 31) - or (prefix.family == 6 and prefix.mask_length >= 127) - ): - # Pool, IPv4 /31-/32 or IPv6 /127-/128 sets are fully usable - first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first) - last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last) - elif prefix.family == 4: - # Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31 - first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first + 1) - last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last - 1) - else: - # For IPv6 prefixes, omit the Subnet-Router anycast address (RFC 4291) - first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first + 1) - last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last) + first_ip_in_prefix, last_ip_in_prefix = prefix.usable_ip_bounds if not records: return [ @@ -195,7 +181,7 @@ def add_available_vlans(vlans, vlan_group): new_vlans.extend(available_vlans_from_range(vlans, vlan_group, vid_range)) vlans = list(vlans) + new_vlans - vlans.sort(key=lambda v: v.vid if type(v) is VLAN else v['vid']) + vlans.sort(key=lambda v: v['vid'] if isinstance(v, dict) else v.vid) return vlans @@ -204,6 +190,9 @@ def rebuild_prefixes(vrf): """ Rebuild the prefix hierarchy for all prefixes in the specified VRF (or global table). """ + Prefix = apps.get_model('ipam', 'Prefix') + prefix_queryset = Prefix.objects.filter(vrf=vrf) + def contains(parent, child): return child in parent and child != parent @@ -219,10 +208,10 @@ def rebuild_prefixes(vrf): stack = [] update_queue = [] - prefixes = Prefix.objects.filter(vrf=vrf).values('pk', 'prefix') + prefixes = prefix_queryset.order_by('prefix', 'pk').values('pk', 'prefix') - # Iterate through all Prefixes in the VRF, growing and shrinking the stack as we go - for i, p in enumerate(prefixes): + # Iterate through all Prefixes in the table, growing and shrinking the stack as we go + for p in prefixes: # Grow the stack if this is a child of the most recent prefix if not stack or contains(stack[-1]['prefix'], p['prefix']): diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 41f5a1832..3c57b8598 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -547,8 +547,64 @@ class AggregateView(generic.ObjectView): ) +class ChildAvailabilityMixin: + """ + Mixin for ObjectChildrenView subclasses that render synthetic "available" rows + (available IP space, prefixes, or VLANs) and must suppress them when the child + queryset has been narrowed by a direct or saved filter. + """ + + @staticmethod + def _where_signature(queryset): + # query.where is Django-internal, but it is the closest signal for "narrowed by a filter". + return str(queryset.query.where) + + def _set_children_filtered(self, is_filtered): + self._child_queryset_is_filtered = is_filtered + return is_filtered + + def _queryset_is_filtered(self, request, queryset, parent): + """ + Return True if the filtered child queryset differs from the unfiltered one. + + Compares WHERE clauses rather than testing queryset.query.where for truthiness, + because child querysets are already scoped to their parent object and carry WHERE + clauses before any user filter is applied. The result is cached on the view instance + so get_extra_context() can reuse it without rebuilding the queryset. + """ + if self.filterset is None: + return self._set_children_filtered(False) + + unfiltered = self.get_children(request, parent) + + return self._set_children_filtered( + self._where_signature(queryset) != self._where_signature(unfiltered) + ) + + def _children_are_filtered(self, request, parent): + """ + Return whether child objects are filtered. + + In the normal ObjectChildrenView flow prep_table_data() runs first and caches the + result, so this returns the cached value. Fall back to rebuilding the queryset for + direct calls where prep_table_data() has not run. + """ + if hasattr(self, '_child_queryset_is_filtered'): + return self._child_queryset_is_filtered + + if self.filterset is None: + return self._set_children_filtered(False) + + unfiltered = self.get_children(request, parent) + filtered = self.filterset(request.GET, unfiltered, request=request).qs + + return self._set_children_filtered( + self._where_signature(filtered) != self._where_signature(unfiltered) + ) + + @register_model_view(Aggregate, 'prefixes') -class AggregatePrefixesView(generic.ObjectChildrenView): +class AggregatePrefixesView(ChildAvailabilityMixin, generic.ObjectChildrenView): queryset = Aggregate.objects.all() child_model = Prefix table = tables.PrefixTable @@ -572,13 +628,21 @@ class AggregatePrefixesView(generic.ObjectChildrenView): show_available = bool(request.GET.get('show_available', 'true') == 'true') show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true') + if self._queryset_is_filtered(request, queryset, parent): + show_available = False + return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned) def get_extra_context(self, request, instance): + show_available = ( + bool(request.GET.get('show_available', 'true') == 'true') and + not self._children_are_filtered(request, instance) + ) + return { 'bulk_querystring': f'within={instance.prefix}', 'first_available_prefix': instance.get_first_available_prefix(), - 'show_available': bool(request.GET.get('show_available', 'true') == 'true'), + 'show_available': show_available, 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'), } @@ -770,7 +834,7 @@ class PrefixView(generic.ObjectView): @register_model_view(Prefix, 'prefixes') -class PrefixPrefixesView(generic.ObjectChildrenView): +class PrefixPrefixesView(ChildAvailabilityMixin, generic.ObjectChildrenView): queryset = Prefix.objects.all() child_model = Prefix table = tables.PrefixTable @@ -794,13 +858,21 @@ class PrefixPrefixesView(generic.ObjectChildrenView): show_available = bool(request.GET.get('show_available', 'true') == 'true') show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true') + if self._queryset_is_filtered(request, queryset, parent): + show_available = False + return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned) def get_extra_context(self, request, instance): + show_available = ( + bool(request.GET.get('show_available', 'true') == 'true') and + not self._children_are_filtered(request, instance) + ) + return { 'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}", 'first_available_prefix': instance.get_first_available_prefix(), - 'show_available': bool(request.GET.get('show_available', 'true') == 'true'), + 'show_available': show_available, 'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'), } @@ -833,7 +905,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView): @register_model_view(Prefix, 'ipaddresses', path='ip-addresses') -class PrefixIPAddressesView(generic.ObjectChildrenView): +class PrefixIPAddressesView(ChildAvailabilityMixin, generic.ObjectChildrenView): queryset = Prefix.objects.all() child_model = IPAddress table = tables.AnnotatedIPAddressTable @@ -851,9 +923,10 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group') def prep_table_data(self, request, queryset, parent): - if not request.GET.get('q') and not get_table_ordering(request, self.table): + if not self._queryset_is_filtered(request, queryset, parent) and not get_table_ordering(request, self.table): return annotate_ip_space(parent) - return queryset + + return super().prep_table_data(request, queryset, parent) def get_extra_context(self, request, instance): return { @@ -1292,7 +1365,7 @@ class VLANGroupBulkDeleteView(generic.BulkDeleteView): @register_model_view(VLANGroup, 'vlans') -class VLANGroupVLANsView(generic.ObjectChildrenView): +class VLANGroupVLANsView(ChildAvailabilityMixin, generic.ObjectChildrenView): queryset = VLANGroup.objects.all() child_model = VLAN table = tables.VLANTable @@ -1312,9 +1385,11 @@ class VLANGroupVLANsView(generic.ObjectChildrenView): ) def prep_table_data(self, request, queryset, parent): - if not get_table_ordering(request, self.table): + # Skip synthetic available rows under active filters: filtered-out VLANs would otherwise look available. + if not self._queryset_is_filtered(request, queryset, parent) and not get_table_ordering(request, self.table): return add_available_vlans(queryset, parent) - return queryset + + return super().prep_table_data(request, queryset, parent) # @@ -1718,6 +1793,16 @@ class VLANDeleteView(generic.ObjectDeleteView): queryset = VLAN.objects.all() +@register_model_view(VLAN, 'bulk_add', path='bulk-add', detail=False) +class VLANBulkCreateView(generic.BulkCreateView): + queryset = VLAN.objects.all() + form = forms.VLANIDBulkCreateForm + model_form = forms.VLANBulkAddForm + pattern_target = 'vid' + pattern_template_fields = ('name',) + template_name = 'ipam/vlan_bulk_add.html' + + @register_model_view(VLAN, 'bulk_import', path='import', detail=False) class VLANBulkImportView(generic.BulkImportView): queryset = VLAN.objects.all() diff --git a/netbox/netbox/api/serializers/bulk.py b/netbox/netbox/api/serializers/bulk.py index c9fd28534..fba4961b0 100644 --- a/netbox/netbox/api/serializers/bulk.py +++ b/netbox/netbox/api/serializers/bulk.py @@ -1,11 +1,79 @@ +import copy +import functools + from rest_framework import serializers from .features import ChangeLogMessageSerializer __all__ = ( 'BulkOperationSerializer', + 'BulkPartialUpdateSchemaMixin', + 'BulkUpdateSchemaMixin', + 'get_bulk_update_serializer_class' ) class BulkOperationSerializer(ChangeLogMessageSerializer): id = serializers.IntegerField() + + +class BulkUpdateSchemaMixin: + def get_fields(self): + fields = super().get_fields() + # Reuse the runtime bulk-operation ID field so the schema stays in sync + # with the validator that consumes `id` before model serialization. + _id = copy.deepcopy(BulkOperationSerializer().fields['id']) + _id.required = True + fields['id'] = _id + + return fields + + +class BulkPartialUpdateSchemaMixin(BulkUpdateSchemaMixin): + def get_fields(self): + fields = super().get_fields() + + for name, field in fields.items(): + if name != 'id': + field.required = False + + return fields + + +@functools.cache +def get_bulk_update_serializer_class(serializer_class, *, partial=False): + """ + Return a schema-only serializer for bulk PUT/PATCH requests. + + Bulk update requests to a list endpoint require each object to include + the target object's numeric ID, even though `id` is read-only on the + normal model serializer. The runtime code consumes `id` before invoking + the model serializer for each object. + """ + + meta = getattr(serializer_class, 'Meta') + + if meta.fields == '__all__': + fields = '__all__' + else: + fields = ('id', *[f for f in meta.fields if f != 'id']) + + class Meta(meta): + pass + + # intentional; this is different than setting fields = fields within class Meta above + Meta.fields = fields + + bases = ( + (BulkPartialUpdateSchemaMixin, serializer_class) + if partial + else (BulkUpdateSchemaMixin, serializer_class) + ) + + attrs = { + 'Meta': Meta, + '__module__': serializer_class.__module__, + } + + prefix = 'PatchedBulk' if partial else 'Bulk' + return type(f'{prefix}{serializer_class.__name__}', bases, attrs) diff --git a/netbox/netbox/api/viewsets/mixins.py b/netbox/netbox/api/viewsets/mixins.py index d05c6abc2..f5e6c18b5 100644 --- a/netbox/netbox/api/viewsets/mixins.py +++ b/netbox/netbox/api/viewsets/mixins.py @@ -10,6 +10,7 @@ from rest_framework.reverse import reverse from core.models import ObjectType from extras.models import ExportTemplate from netbox.api.serializers import BulkOperationSerializer +from netbox.api.serializers.bulk import get_bulk_update_serializer_class from netbox.jobs import AsyncAPIJob from utilities.exceptions import RQWorkerNotRunningException from utilities.rqworker import any_workers_for_queue @@ -240,6 +241,20 @@ class BulkUpdateModelMixin: return updated_pks + def get_bulk_update_serializer_class(self, *, partial=False): + return get_bulk_update_serializer_class( + self.get_serializer_class(), + partial=partial, + ) + + def get_bulk_update_request_serializer(self, *, partial=False): + serializer_class = self.get_bulk_update_serializer_class(partial=partial) + + # Important: do NOT pass partial=True here. The partial schema class already + # makes non-id fields optional, and passing partial=True would also make id + # appear optional in OpenAPI. + return serializer_class(many=True) + def bulk_partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.bulk_update(request, *args, **kwargs) diff --git a/netbox/netbox/graphql/filter_lookups.py b/netbox/netbox/graphql/filter_lookups.py index 752e0e966..e56c3048b 100644 --- a/netbox/netbox/graphql/filter_lookups.py +++ b/netbox/netbox/graphql/filter_lookups.py @@ -1,3 +1,4 @@ +import re from enum import Enum from typing import Generic, TypeVar @@ -11,17 +12,48 @@ from strawberry.directive import DirectiveValue from strawberry.types import Info from strawberry_django import ( ComparisonFilterLookup, - DateFilterLookup, - DatetimeFilterLookup, FilterLookup, RangeLookup, - StrFilterLookup, - TimeFilterLookup, process_filters, ) from netbox.graphql.scalars import BigInt +# ------------------------------------------------------------------ +# JSON path validation (VM-323) +# ------------------------------------------------------------------ + +# Each segment of a JSON path may only contain alphanumerics, underscores, and +# hyphens. Hyphens are included because JSON keys commonly use them; leading +# underscores are permitted (e.g. _foo is a valid key name). +_JSON_PATH_SEGMENT_RE = re.compile(r'^[A-Za-z0-9_][A-Za-z0-9_-]*$') + + +def _validate_json_path(path: str) -> str: + """Validate a JSON traversal path for use in ORM lookups. + + Each ``__``-separated segment must match ``[A-Za-z0-9_][A-Za-z0-9_-]*``. + Raises ``ValueError`` on an empty path, empty segment, or segment with + disallowed characters. + + ORM operator names (``date``, ``regex``, etc.) are intentionally *not* + blocked here: ``JSONFilter.filter()`` always appends ``__`` to the path + before handing it to ``process_filters``, so a segment named ``regex`` + becomes another level of JSON key traversal (``data__key__regex__exact``), + not the ORM regex transform (``data__key__regex=…``). + """ + if not path: + raise ValueError("JSON path cannot be empty") + + for segment in path.split('__'): + if not segment: + raise ValueError("JSON path contains consecutive or trailing '__'") + if not _JSON_PATH_SEGMENT_RE.match(segment): + raise ValueError(f"Invalid JSON path segment: {segment!r}") + + return path + + __all__ = ( 'ArrayLookup', 'BigIntegerLookup', @@ -31,6 +63,8 @@ __all__ = ( 'IntegerLookup', 'IntegerRangeArrayLookup', 'JSONFilter', + 'JSONLookup', + 'JSONStringLookup', 'StringArrayLookup', 'TreeNodeFilter', ) @@ -39,16 +73,82 @@ T = TypeVar('T') SKIP_MSG = 'Filter will be skipped on `null` value' +# These JSON lookup types intentionally mirror the legacy DateFilterLookup[str], +# TimeFilterLookup[str], and DatetimeFilterLookup[str] schema. JSON values are +# string-backed, so the concrete strawberry-django date/time lookup classes +# (which now ignore type parameters and warn) are deliberately not used here. +@strawberry.input(name='StrDateFilterLookup') +class JSONDateFilterLookup(ComparisonFilterLookup[str]): + year: ComparisonFilterLookup[int] | None = strawberry.UNSET + month: ComparisonFilterLookup[int] | None = strawberry.UNSET + day: ComparisonFilterLookup[int] | None = strawberry.UNSET + week_day: ComparisonFilterLookup[int] | None = strawberry.UNSET + iso_week_day: ComparisonFilterLookup[int] | None = strawberry.UNSET + week: ComparisonFilterLookup[int] | None = strawberry.UNSET + iso_year: ComparisonFilterLookup[int] | None = strawberry.UNSET + quarter: ComparisonFilterLookup[int] | None = strawberry.UNSET + + +@strawberry.input(name='StrTimeFilterLookup') +class JSONTimeFilterLookup(ComparisonFilterLookup[str]): + hour: ComparisonFilterLookup[int] | None = strawberry.UNSET + minute: ComparisonFilterLookup[int] | None = strawberry.UNSET + second: ComparisonFilterLookup[int] | None = strawberry.UNSET + date: ComparisonFilterLookup[int] | None = strawberry.UNSET + time: ComparisonFilterLookup[int] | None = strawberry.UNSET + + +@strawberry.input(name='StrDatetimeFilterLookup') +class JSONDatetimeFilterLookup(ComparisonFilterLookup[str]): + year: ComparisonFilterLookup[int] | None = strawberry.UNSET + month: ComparisonFilterLookup[int] | None = strawberry.UNSET + day: ComparisonFilterLookup[int] | None = strawberry.UNSET + week_day: ComparisonFilterLookup[int] | None = strawberry.UNSET + iso_week_day: ComparisonFilterLookup[int] | None = strawberry.UNSET + week: ComparisonFilterLookup[int] | None = strawberry.UNSET + iso_year: ComparisonFilterLookup[int] | None = strawberry.UNSET + quarter: ComparisonFilterLookup[int] | None = strawberry.UNSET + hour: ComparisonFilterLookup[int] | None = strawberry.UNSET + minute: ComparisonFilterLookup[int] | None = strawberry.UNSET + second: ComparisonFilterLookup[int] | None = strawberry.UNSET + date: ComparisonFilterLookup[int] | None = strawberry.UNSET + time: ComparisonFilterLookup[int] | None = strawberry.UNSET + + +@strawberry.input(description='String lookups for JSON field values.') +class JSONStringLookup: + """ + String-filter type for use inside JSONLookup. + + Equivalent to ``StrFilterLookup`` but defined explicitly so that the type + name remains stable and any future per-field restrictions are easy to add. + ``regex`` / ``i_regex`` are included: they provide no additional oracle + power beyond ``starts_with``, which is also present. + """ + exact: str | None = strawberry_django.filter_field() + i_exact: str | None = strawberry_django.filter_field() + contains: str | None = strawberry_django.filter_field() + i_contains: str | None = strawberry_django.filter_field() + starts_with: str | None = strawberry_django.filter_field() + i_starts_with: str | None = strawberry_django.filter_field() + ends_with: str | None = strawberry_django.filter_field() + i_ends_with: str | None = strawberry_django.filter_field() + in_: list[str] | None = strawberry_django.filter_field() + isnull: bool | None = strawberry_django.filter_field() + regex: str | None = strawberry_django.filter_field() + i_regex: str | None = strawberry_django.filter_field() + + @strawberry.input(one_of=True, description='Lookup for JSON field. Only one of the lookup fields can be set.') class JSONLookup: - string_lookup: StrFilterLookup[str] | None = strawberry_django.filter_field() + string_lookup: JSONStringLookup | None = strawberry_django.filter_field() int_range_lookup: RangeLookup[int] | None = strawberry_django.filter_field() int_comparison_lookup: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() float_range_lookup: RangeLookup[float] | None = strawberry_django.filter_field() float_comparison_lookup: ComparisonFilterLookup[float] | None = strawberry_django.filter_field() - date_lookup: DateFilterLookup[str] | None = strawberry_django.filter_field() - datetime_lookup: DatetimeFilterLookup[str] | None = strawberry_django.filter_field() - time_lookup: TimeFilterLookup[str] | None = strawberry_django.filter_field() + date_lookup: JSONDateFilterLookup | None = strawberry_django.filter_field() + datetime_lookup: JSONDatetimeFilterLookup | None = strawberry_django.filter_field() + time_lookup: JSONTimeFilterLookup | None = strawberry_django.filter_field() boolean_lookup: FilterLookup[bool] | None = strawberry_django.filter_field() def get_filter(self): @@ -119,7 +219,12 @@ class JSONFilter: if not filters: return queryset, Q() - json_path = f'{prefix}{self.path}__' + try: + safe_path = _validate_json_path(self.path) + except ValueError: + return queryset, Q() + + json_path = f'{prefix}{safe_path}__' return process_filters(filters=filters, queryset=queryset, info=info, prefix=json_path) diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index 94b836f06..c33e3e8b9 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from datetime import datetime from typing import TYPE_CHECKING, Annotated, TypeVar import strawberry @@ -48,9 +47,9 @@ class SyncedDataFilterMixin: strawberry_django.filter_field() ) data_file_id: FilterLookup[int] | None = strawberry_django.filter_field() - data_path: StrFilterLookup[str] | None = strawberry_django.filter_field() + data_path: StrFilterLookup | None = strawberry_django.filter_field() auto_sync_enabled: FilterLookup[bool] | None = strawberry_django.filter_field() - data_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + data_synced: DatetimeFilterLookup | None = strawberry_django.filter_field() @dataclass diff --git a/netbox/netbox/graphql/filters.py b/netbox/netbox/graphql/filters.py index a372d7bc0..c6241e226 100644 --- a/netbox/netbox/graphql/filters.py +++ b/netbox/netbox/graphql/filters.py @@ -42,21 +42,21 @@ class NetBoxModelFilter( @dataclass class NestedGroupModelFilter(NetBoxModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() parent_id: ID | None = strawberry_django.filter_field() @dataclass class OrganizationalModelFilter(NetBoxModelFilter): - name: StrFilterLookup[str] | None = strawberry_django.filter_field() - slug: StrFilterLookup[str] | None = strawberry_django.filter_field() - description: StrFilterLookup[str] | None = strawberry_django.filter_field() - comments: StrFilterLookup[str] | None = strawberry_django.filter_field() + name: StrFilterLookup | None = strawberry_django.filter_field() + slug: StrFilterLookup | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() + comments: StrFilterLookup | None = strawberry_django.filter_field() @dataclass class PrimaryModelFilter(NetBoxModelFilter): - description: StrFilterLookup[str] | None = strawberry_django.filter_field() - comments: StrFilterLookup[str] | None = strawberry_django.filter_field() + description: StrFilterLookup | None = strawberry_django.filter_field() + comments: StrFilterLookup | None = strawberry_django.filter_field() diff --git a/netbox/netbox/graphql/schema.py b/netbox/netbox/graphql/schema.py index 1cce4a7dc..6b432050f 100644 --- a/netbox/netbox/graphql/schema.py +++ b/netbox/netbox/graphql/schema.py @@ -1,3 +1,5 @@ +from collections.abc import Callable + import strawberry from django.conf import settings from strawberry.extensions import MaxAliasesLimiter, QueryDepthLimiter, SchemaExtension @@ -18,6 +20,8 @@ from wireless.graphql.schema import WirelessQuery from .scalars import BigInt, BigIntScalar +SchemaExtensionFactory = type[SchemaExtension] | Callable[[], SchemaExtension] + @strawberry.type class Query( @@ -36,14 +40,16 @@ class Query( pass -def get_schema_extensions() -> list[SchemaExtension]: - extensions: list[SchemaExtension] = [ - DjangoOptimizerExtension(prefetch_custom_queryset=True), - MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES), - ] +def get_schema_extensions() -> list[SchemaExtensionFactory]: + max_aliases = settings.GRAPHQL_MAX_ALIASES max_depth = settings.GRAPHQL_MAX_QUERY_DEPTH + + extensions: list[SchemaExtensionFactory] = [ + lambda: DjangoOptimizerExtension(prefetch_custom_queryset=True), + lambda: MaxAliasesLimiter(max_alias_count=max_aliases), + ] if max_depth and max_depth > 0: - extensions.append(QueryDepthLimiter(max_depth=max_depth)) + extensions.append(lambda: QueryDepthLimiter(max_depth=max_depth)) return extensions diff --git a/netbox/netbox/middleware.py b/netbox/netbox/middleware.py index 4a36c2d98..a9ee93fc6 100644 --- a/netbox/netbox/middleware.py +++ b/netbox/netbox/middleware.py @@ -10,7 +10,9 @@ from django.db import ProgrammingError, connection from django.db.utils import InternalError from django.http import Http404, HttpResponseRedirect from django.middleware.common import CommonMiddleware as DjangoCommonMiddleware +from django.utils.translation import gettext_lazy as _ from django_prometheus import middleware +from social_django.middleware import SocialAuthExceptionMiddleware as SocialAuthExceptionMiddleware_ from netbox.config import clear_config, get_config from netbox.metrics import Metrics @@ -26,6 +28,7 @@ __all__ = ( 'PrometheusAfterMiddleware', 'PrometheusBeforeMiddleware', 'RemoteUserMiddleware', + 'SocialAuthExceptionMiddleware', ) @@ -286,3 +289,13 @@ class MaintenanceModeMiddleware: messages.error(request, error_message) return HttpResponseRedirect(request.path_info) return None + + +class SocialAuthExceptionMiddleware(SocialAuthExceptionMiddleware_): + """ + Subclass of python-social-auth's exception middleware which surfaces a generic, user-friendly + message rather than exposing the raw social_core exception text to (typically unauthenticated) + users when an SSO/SAML login fails. + """ + def get_message(self, request, exception): + return _("Single sign-on failed. Please try again or contact your administrator.") diff --git a/netbox/netbox/models/deletion.py b/netbox/netbox/models/deletion.py index e106fbc10..86ac2bfd7 100644 --- a/netbox/netbox/models/deletion.py +++ b/netbox/netbox/models/deletion.py @@ -3,6 +3,7 @@ import logging from django.contrib.contenttypes.fields import GenericRelation from django.db import router from django.db.models.deletion import CASCADE, Collector +from django.utils.translation import gettext as _ logger = logging.getLogger("netbox.models.deletion") @@ -45,7 +46,7 @@ class CustomCollector(Collector): # Add GenericRelations to the dependency graph processed_relations = set() - for _, instances in list(self.data.items()): + for _model, instances in list(self.data.items()): for instance in instances: # Get all GenericRelations for this model for field in instance._meta.private_fields: @@ -70,10 +71,13 @@ class DeleteMixin: Override delete to use our custom collector. """ using = using or router.db_for_write(self.__class__, instance=self) - assert self._get_pk_val() is not None, ( - f"{self._meta.object_name} object can't be deleted because its " - f"{self._meta.pk.attname} attribute is set to None." - ) + if self._get_pk_val() is None: + raise ValueError( + _("{object_name} object can't be deleted because its {pk_attname} attribute is set to None.").format( + object_name=self._meta.object_name, + pk_attname=self._meta.pk.attname, + ) + ) collector = CustomCollector(using=using) collector.collect([self], keep_parents=keep_parents) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 092ac44db..0ae705127 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -514,6 +514,7 @@ MIDDLEWARE = [ 'netbox.middleware.RemoteUserMiddleware', 'netbox.middleware.CoreMiddleware', 'netbox.middleware.MaintenanceModeMiddleware', + 'netbox.middleware.SocialAuthExceptionMiddleware', ] if DEBUG: @@ -713,6 +714,13 @@ SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.user.user_details', ) +# Redirect users back to the login page (surfacing the error via the messages framework) when an +# SSO/SAML authentication failure occurs, rather than raising an HTTP 500. Full exceptions are still +# raised when DEBUG is enabled. LOGIN_URL is an absolute path which respects BASE_PATH; the social +# auth middleware passes this value directly to an HttpResponseRedirect without reversing it. +SOCIAL_AUTH_LOGIN_ERROR_URL = LOGIN_URL +SOCIAL_AUTH_RAISE_EXCEPTIONS = DEBUG + # Load all SOCIAL_AUTH_* settings from the user configuration for param in dir(configuration): if param.startswith('SOCIAL_AUTH_'): diff --git a/netbox/netbox/tests/test_authentication.py b/netbox/netbox/tests/test_authentication.py index 55298cb2f..5221fe4b8 100644 --- a/netbox/netbox/tests/test_authentication.py +++ b/netbox/netbox/tests/test_authentication.py @@ -1,13 +1,16 @@ import datetime from django.conf import settings -from django.test import Client +from django.contrib.messages.storage.fallback import FallbackStorage +from django.test import Client, RequestFactory, SimpleTestCase from django.test.utils import override_settings from django.urls import reverse from rest_framework.test import APIClient +from social_core.exceptions import AuthFailed from core.models import ObjectType from dcim.models import Rack, Site +from netbox.middleware import SocialAuthExceptionMiddleware from users.constants import TOKEN_PREFIX from users.models import Group, ObjectPermission, Token, User from utilities.testing import TestCase @@ -697,3 +700,56 @@ class ObjectPermissionAPIViewTestCase(TestCase): url = reverse('dcim-api:rack-detail', kwargs={'pk': self.racks[0].pk}) response = self.client.delete(url, format='json', **self.header) self.assertEqual(response.status_code, 204) + + +class SocialAuthExceptionMiddlewareTestCase(SimpleTestCase): + """ + Verify that SSO/SAML authentication failures are surfaced as a login-page message rather than + bubbling up as an HTTP 500 (see #22346). + """ + GENERIC_MESSAGE = "Single sign-on failed. Please try again or contact your administrator." + + class FakeStrategy: + # Mirror social_core's DjangoStrategy.setting(), which reads SOCIAL_AUTH_ from Django + # settings. This ensures the test exercises the real configured values (e.g. + # SOCIAL_AUTH_LOGIN_ERROR_URL) rather than hardcoded stand-ins. + def setting(self, name, default=None, backend=None): + return getattr(settings, f'SOCIAL_AUTH_{name}', default) + + class FakeBackend: + name = 'saml' + + def setUp(self): + self.factory = RequestFactory() + self.middleware = SocialAuthExceptionMiddleware(lambda request: None) + + def _make_request(self): + request = self.factory.get('/') + request.social_strategy = self.FakeStrategy() + request.backend = self.FakeBackend() + # Attach message storage (normally provided by MessageMiddleware) + setattr(request, 'session', {}) + request._messages = FallbackStorage(request) + return request + + def test_generic_message(self): + """ + The raw exception text should never be surfaced to the user. + """ + request = self._make_request() + exception = AuthFailed(self.FakeBackend(), 'raw internal SAML detail') + self.assertEqual(self.middleware.get_message(request, exception), self.GENERIC_MESSAGE) + + def test_redirect_on_failure(self): + """ + A SocialAuthBaseException should redirect to the login page with the generic message set. + """ + request = self._make_request() + exception = AuthFailed(self.FakeBackend(), 'raw internal SAML detail') + response = self.middleware.process_exception(request, exception) + + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, settings.SOCIAL_AUTH_LOGIN_ERROR_URL) + self.assertEqual(response.url, settings.LOGIN_URL) + messages = [str(m) for m in request._messages] + self.assertEqual(messages, [self.GENERIC_MESSAGE]) diff --git a/netbox/netbox/tests/test_graphql.py b/netbox/netbox/tests/test_graphql.py index 2df3a7f98..e65aca00f 100644 --- a/netbox/netbox/tests/test_graphql.py +++ b/netbox/netbox/tests/test_graphql.py @@ -1,4 +1,5 @@ import json +import re import strawberry from django.contrib.contenttypes.models import ContentType @@ -10,15 +11,18 @@ from strawberry.schema.config import StrawberryConfig from dcim.choices import LocationStatusChoices from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Site, VirtualChassis -from extras.models import TableConfig +from extras.models import TableConfig, Tag from netbox.graphql.scalars import BigInt, BigIntScalar -from netbox.graphql.schema import Query, get_schema_extensions +from netbox.graphql.schema import Query, get_schema_extensions, schema from utilities.tables import get_table_for_model from utilities.testing import APITestCase, TestCase, disable_warnings class GraphQLTestCase(TestCase): + def _schema_extension_instances(self): + return [factory() for factory in get_schema_extensions()] + @override_settings(GRAPHQL_ENABLED=False) def test_graphql_enabled(self): """ @@ -32,21 +36,21 @@ class GraphQLTestCase(TestCase): """ QueryDepthLimiter should not be installed when GRAPHQL_MAX_QUERY_DEPTH is unset. """ - self.assertFalse(any(isinstance(ext, QueryDepthLimiter) for ext in get_schema_extensions())) + self.assertFalse(any(isinstance(ext, QueryDepthLimiter) for ext in self._schema_extension_instances())) @override_settings(GRAPHQL_MAX_QUERY_DEPTH=0) def test_graphql_max_query_depth_disabled_when_zero(self): """ QueryDepthLimiter should not be installed when GRAPHQL_MAX_QUERY_DEPTH is zero. """ - self.assertFalse(any(isinstance(ext, QueryDepthLimiter) for ext in get_schema_extensions())) + self.assertFalse(any(isinstance(ext, QueryDepthLimiter) for ext in self._schema_extension_instances())) @override_settings(GRAPHQL_MAX_QUERY_DEPTH=-1) def test_graphql_max_query_depth_disabled_when_negative(self): """ QueryDepthLimiter should not be installed when GRAPHQL_MAX_QUERY_DEPTH is negative. """ - self.assertFalse(any(isinstance(ext, QueryDepthLimiter) for ext in get_schema_extensions())) + self.assertFalse(any(isinstance(ext, QueryDepthLimiter) for ext in self._schema_extension_instances())) @override_settings(GRAPHQL_MAX_QUERY_DEPTH=3) def test_graphql_max_query_depth_enforced(self): @@ -54,9 +58,9 @@ class GraphQLTestCase(TestCase): Queries exceeding GRAPHQL_MAX_QUERY_DEPTH should be rejected. """ extensions = get_schema_extensions() - self.assertTrue(any(isinstance(ext, QueryDepthLimiter) for ext in extensions)) + self.assertTrue(any(isinstance(ext, QueryDepthLimiter) for ext in self._schema_extension_instances())) - # Build a temporary schema with the configured extensions and execute a deep query + # Build a temporary schema with the configured extension factories and execute a deep query test_schema = strawberry.Schema( query=Query, config=StrawberryConfig(auto_camel_case=False, scalar_map={BigInt: BigIntScalar}), @@ -87,6 +91,30 @@ class GraphQLTestCase(TestCase): with disable_warnings('django.request'): self.assertHttpStatus(response, 302) # Redirect to login page + def test_json_lookup_schema_is_string_backed(self): + """JSONLookup date/time lookups keep the legacy string-backed input types and fields.""" + sdl = schema.as_str() + + def input_block(name): + match = re.search(rf'^input {re.escape(name)}\b.*?^\}}', sdl, re.DOTALL | re.MULTILINE) + self.assertIsNotNone(match, f'{name} not found in schema') + return match.group(0) + + # JSONLookup points at the legacy string-backed lookup type names + json_lookup = input_block('JSONLookup') + self.assertIn('date_lookup: StrDateFilterLookup', json_lookup) + self.assertIn('datetime_lookup: StrDatetimeFilterLookup', json_lookup) + self.assertIn('time_lookup: StrTimeFilterLookup', json_lookup) + + # Value fields are string-backed, not Date/DateTime/Time scalars + self.assertIn('exact: String', input_block('StrDateFilterLookup')) + + # Legacy date/time sub-lookups remain integer comparison lookups + for name in ('StrTimeFilterLookup', 'StrDatetimeFilterLookup'): + block = input_block(name) + self.assertIn('date: IntComparisonFilterLookup', block) + self.assertIn('time: IntComparisonFilterLookup', block) + class GraphQLAPITestCase(APITestCase): @@ -185,6 +213,72 @@ class GraphQLAPITestCase(APITestCase): self.assertNotIn('errors', data) self.assertEqual(len(data['data']['site']['locations']), 0) + @override_settings(LOGIN_REQUIRED=True) + def test_graphql_nested_filter_objects(self): + """ + Test filtering of nested GraphQL object lists. + """ + self.add_permissions('dcim.view_site', 'dcim.view_location', 'extras.view_tag') + + site = Site.objects.create( + name='Nested Filter Site', + slug='nested-filter-site' + ) + + # Location is MPTT-managed; bulk_create skips tree-init hooks. Use per-instance create. + Location.objects.create( + site=site, + name='Nested Active 1', + slug='nested-active-1', + status=LocationStatusChoices.STATUS_ACTIVE, + ) + Location.objects.create( + site=site, + name='Nested Active 2', + slug='nested-active-2', + status=LocationStatusChoices.STATUS_ACTIVE, + ) + Location.objects.create( + site=site, + name='Nested Planned', + slug='nested-planned', + status=LocationStatusChoices.STATUS_PLANNED, + ) + + planned = Tag.objects.create(name='Planned', slug='planned') + production = Tag.objects.create(name='Production', slug='production') + staging = Tag.objects.create(name='Staging', slug='staging') + site.tags.add(planned, production, staging) + + url = reverse('graphql') + query = f""" + {{ + site(id: {site.pk}) {{ + locations(filters: {{status: {{exact: STATUS_ACTIVE}}}}) {{ + name + }} + tags(filters: {{name: {{i_starts_with: "P"}}}}) {{ + name + }} + }} + }} + """ + + response = self.client.post(url, data={'query': query}, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + + data = json.loads(response.content) + self.assertNotIn('errors', data) + + self.assertEqual( + {location['name'] for location in data['data']['site']['locations']}, + {'Nested Active 1', 'Nested Active 2'} + ) + self.assertEqual( + {tag['name'] for tag in data['data']['site']['tags']}, + {'Planned', 'Production'} + ) + def test_graphql_integer_range_lookup(self): """ Test that range_lookup works for integer fields (e.g. vc_position). Regression test for #20468. @@ -411,3 +505,97 @@ class GraphQLAPITestCase(APITestCase): data = json.loads(response.content) self.assertIn('errors', data) self.assertEqual(data['errors'][0]['message'], 'Cannot specify both `start` and `offset` in pagination.') + + +class JSONPathValidationTestCase(TestCase): + """Unit tests for _validate_json_path (VM-323 security fix).""" + + def setUp(self): + from netbox.graphql.filter_lookups import _validate_json_path + self.validate = _validate_json_path + + # --- Valid paths --- + + def test_single_key(self): + self.assertEqual(self.validate('key'), 'key') + + def test_nested_key(self): + self.assertEqual(self.validate('parent__child'), 'parent__child') + + def test_deeply_nested(self): + self.assertEqual(self.validate('a__b__c'), 'a__b__c') + + def test_key_with_underscores(self): + self.assertEqual(self.validate('my_key'), 'my_key') + + def test_key_with_hyphens(self): + self.assertEqual(self.validate('my-key'), 'my-key') + + def test_numeric_array_index(self): + self.assertEqual(self.validate('items__0'), 'items__0') + + def test_alphanumeric_segment(self): + self.assertEqual(self.validate('key123'), 'key123') + + def test_key_with_leading_underscore(self): + # JSON keys may start with underscore (e.g. _foo) + self.assertEqual(self.validate('_key'), '_key') + + def test_orm_operator_name_as_key(self): + # 'date', 'regex' etc. are valid JSON key names; the path validator + # must not block them. The ORM injection risk is neutralised by the + # trailing __ that JSONFilter always appends before process_filters. + self.assertEqual(self.validate('date'), 'date') + self.assertEqual(self.validate('key__regex'), 'key__regex') + self.assertEqual(self.validate('key__exact'), 'key__exact') + + # --- Invalid paths --- + + def test_rejects_empty_string(self): + with self.assertRaises(ValueError): + self.validate('') + + def test_rejects_all_underscores(self): + # '___' splits into segments ['', '', ''] via '__' — empty segments rejected + with self.assertRaises(ValueError): + self.validate('___') + + def test_accepts_trailing_single_underscore(self): + # A single trailing underscore is a valid JSON key character + self.assertEqual(self.validate('key_'), 'key_') + + def test_rejects_trailing_double_underscore(self): + with self.assertRaises(ValueError): + self.validate('key__') + + def test_rejects_leading_double_underscore(self): + with self.assertRaises(ValueError): + self.validate('__key') + + def test_rejects_consecutive_double_underscores(self): + with self.assertRaises(ValueError): + self.validate('key1____key2') + + def test_rejects_segment_starting_with_special_char(self): + with self.assertRaises(ValueError): + self.validate('$secret') + + def test_rejects_path_with_spaces(self): + with self.assertRaises(ValueError): + self.validate('key one') + + def test_rejects_path_with_dot(self): + with self.assertRaises(ValueError): + self.validate('key.subkey') + + +class JSONStringLookupTestCase(TestCase): + """Verify JSONStringLookup exposes the expected set of string operators.""" + + def test_string_operators_present(self): + from netbox.graphql.filter_lookups import JSONStringLookup + field_names = {f.name for f in JSONStringLookup.__strawberry_definition__.fields} + for expected in ('exact', 'i_exact', 'contains', 'i_contains', + 'starts_with', 'i_starts_with', 'ends_with', 'i_ends_with', + 'in_', 'isnull', 'regex', 'i_regex'): + self.assertIn(expected, field_names, f"{expected!r} must be present on JSONStringLookup") diff --git a/netbox/netbox/tests/test_models.py b/netbox/netbox/tests/test_models.py index 0351aea82..d4a931a0d 100644 --- a/netbox/netbox/tests/test_models.py +++ b/netbox/netbox/tests/test_models.py @@ -4,6 +4,7 @@ from django.conf import settings from django.test import TestCase from core.models import ObjectChange +from dcim.models import Site from netbox.tests.dummy_plugin.models import DummyNetBoxModel @@ -21,3 +22,12 @@ class ModelTestCase(TestCase): m.pk = 123 self.assertEqual(m.get_absolute_url(), f'/plugins/dummy-plugin/netboxmodel/{m.pk}/') + + +class DeleteMixinTestCase(TestCase): + + def test_delete_unsaved_instance_raises_value_error(self): + """Deleting an instance with no primary key raises ValueError.""" + site = Site(name='Site 1', slug='site-1') + with self.assertRaises(ValueError): + site.delete() diff --git a/netbox/netbox/tests/test_ui.py b/netbox/netbox/tests/test_ui.py index 55cbb3842..9ecd91ef0 100644 --- a/netbox/netbox/tests/test_ui.py +++ b/netbox/netbox/tests/test_ui.py @@ -3,6 +3,7 @@ from types import SimpleNamespace from django.template import Context, Template from django.test import RequestFactory, SimpleTestCase, TestCase +from netaddr import IPNetwork from circuits.choices import CircuitStatusChoices, VirtualCircuitTerminationRoleChoices from circuits.models import ( @@ -250,6 +251,29 @@ class TextAttrTestCase(TestCase): self.assertTrue(context['copy_button']) +class ArrayAttrTestCase(TestCase): + + def test_get_value(self): + attr = attrs.ArrayAttr('allowed_ips') + obj = SimpleNamespace(allowed_ips=[IPNetwork('192.168.1.1/32'), IPNetwork('2001:db8::/64')]) + self.assertEqual(attr.get_value(obj), '192.168.1.1/32, 2001:db8::/64') + + def test_get_value_empty(self): + attr = attrs.ArrayAttr('allowed_ips') + obj = SimpleNamespace(allowed_ips=[]) + self.assertIsNone(attr.get_value(obj)) + + def test_get_value_none(self): + attr = attrs.ArrayAttr('allowed_ips') + obj = SimpleNamespace(allowed_ips=None) + self.assertIsNone(attr.get_value(obj)) + + def test_get_value_with_format_string(self): + attr = attrs.ArrayAttr('ports', format_string='{}/tcp') + obj = SimpleNamespace(ports=[80, 443]) + self.assertEqual(attr.get_value(obj), '80/tcp, 443/tcp') + + class NumericAttrTestCase(TestCase): def test_get_context_with_unit_accessor(self): diff --git a/netbox/netbox/tests/test_views.py b/netbox/netbox/tests/test_views.py index ee9599dc2..96c31ba16 100644 --- a/netbox/netbox/tests/test_views.py +++ b/netbox/netbox/tests/test_views.py @@ -1,9 +1,13 @@ import urllib.parse +from unittest.mock import patch +from django.contrib.contenttypes.models import ContentType +from django.http import HttpResponse from django.test import Client, override_settings from django.urls import reverse -from dcim.models import Site +from dcim.models import DeviceType, Manufacturer, Site +from extras.models import ImageAttachment from netbox.constants import EMPTY_TABLE_TEXT from netbox.search.backends import search_backend from utilities.testing import TestCase @@ -78,6 +82,27 @@ class SearchViewTestCase(TestCase): class MediaViewTestCase(TestCase): + @classmethod + def setUpTestData(cls): + site = Site.objects.create(name='Site 1', slug='site-1') + ct = ContentType.objects.get_for_model(Site) + cls.image_attachment = ImageAttachment.objects.create( + object_type=ct, + object_id=site.pk, + name='Test Image', + image='image-attachments/site_1_test.jpg', + image_height=100, + image_width=100, + ) + + manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') + cls.device_type = DeviceType.objects.create( + model='Device Type 1', + slug='device-type-1', + manufacturer=manufacturer, + front_image='devicetype-images/front.jpg', + ) + def test_media_login_required(self): url = reverse('media', kwargs={'path': 'foo.txt'}) response = Client().get(url) @@ -92,3 +117,45 @@ class MediaViewTestCase(TestCase): # Unauthenticated request should return a 404 (not found) self.assertHttpStatus(response, 404) + + def test_image_attachment_with_permission(self): + self.add_permissions('extras.view_imageattachment') + url = reverse('media', kwargs={'path': self.image_attachment.image.name}) + with patch('netbox.views.misc.serve', return_value=HttpResponse(status=200)): + response = self.client.get(url) + self.assertHttpStatus(response, 200) + self.assertEqual(response['Content-Disposition'], 'attachment') + self.assertEqual(response['X-Content-Type-Options'], 'nosniff') + + def test_image_attachment_without_permission(self): + url = reverse('media', kwargs={'path': self.image_attachment.image.name}) + response = self.client.get(url) + self.assertHttpStatus(response, 404) + + def test_image_attachment_traversal_without_permission(self): + # A traversal path that normalizes to a protected directory must still be denied. + traversal_path = 'foo/../' + self.image_attachment.image.name + url = reverse('media', kwargs={'path': traversal_path}) + response = self.client.get(url) + self.assertHttpStatus(response, 404) + + def test_device_type_with_permission(self): + self.add_permissions('dcim.view_devicetype') + url = reverse('media', kwargs={'path': self.device_type.front_image.name}) + with patch('netbox.views.misc.serve', return_value=HttpResponse(status=200)): + response = self.client.get(url) + self.assertHttpStatus(response, 200) + self.assertEqual(response['Content-Disposition'], 'attachment') + self.assertEqual(response['X-Content-Type-Options'], 'nosniff') + + def test_device_type_without_permission(self): + url = reverse('media', kwargs={'path': self.device_type.front_image.name}) + response = self.client.get(url) + self.assertHttpStatus(response, 404) + + def test_device_type_traversal_without_permission(self): + # A traversal path that normalizes to a protected directory must still be denied. + traversal_path = 'foo/../' + self.device_type.front_image.name + url = reverse('media', kwargs={'path': traversal_path}) + response = self.client.get(url) + self.assertHttpStatus(response, 404) diff --git a/netbox/netbox/ui/attrs.py b/netbox/netbox/ui/attrs.py index 8734fb4a1..60a14d33a 100644 --- a/netbox/netbox/ui/attrs.py +++ b/netbox/netbox/ui/attrs.py @@ -8,6 +8,7 @@ from utilities.data import resolve_attr_path __all__ = ( 'AddressAttr', + 'ArrayAttr', 'BooleanAttr', 'ChoiceAttr', 'ColorAttr', @@ -141,6 +142,22 @@ class TextAttr(ObjectAttribute): } +class ArrayAttr(TextAttr): + """ + An attribute comprising an array of values, rendered as a comma-separated list. If specified, `format_string` + is applied to each item individually. Null and empty arrays are treated as equivalent: both render as the + placeholder. + """ + + def get_value(self, obj): + value = resolve_attr_path(obj, self.accessor) + if not value: + return None + if self.format_string: + return ', '.join(self.format_string.format(v) for v in value) + return ', '.join(str(v) for v in value) + + class NumericAttr(ObjectAttribute): """ An integer or float attribute. diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 6672ab5a0..9a293a23e 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -8,7 +8,7 @@ from types import SimpleNamespace from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel -from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError +from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured, ObjectDoesNotExist, ValidationError from django.db import IntegrityError, router, transaction from django.db.models import ManyToManyField, ProtectedError, RestrictedError from django.db.models.fields.reverse_related import ManyToManyRel @@ -27,7 +27,7 @@ from netbox.forms.bulk_rename import NetBoxModelBulkRenameForm from netbox.models.features import ChangeLoggingMixin from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename from utilities.error_handlers import handle_protectederror -from utilities.exceptions import AbortRequest, PermissionsViolation +from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation from utilities.export import TableExport, stream_table_csv_response from utilities.forms import BulkDeleteForm, BulkRenameForm, restrict_form_fields from utilities.forms.bulk_import import BulkImportForm @@ -210,7 +210,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): if request.GET['export'] == 'table': table = self.get_table(self.queryset, request, has_table_actions) columns = [name for name, _ in table.selected_columns] - delimiter = request.user.config.get('csv_delimiter') + delimiter = request.user.config.get('csv_delimiter') if request.user.is_authenticated else None return self.export_table(table, columns, delimiter=delimiter) # Render an ExportTemplate @@ -231,7 +231,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): # Fall back to default table/YAML export table = self.get_table(self.queryset, request, has_table_actions) - delimiter = request.user.config.get('csv_delimiter') + delimiter = request.user.config.get('csv_delimiter') if request.user.is_authenticated else None return self.export_table(table, delimiter=delimiter) # Render the objects table @@ -274,11 +274,96 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = None model_form = None pattern_target = '' + pattern_template_fields = () htmx_template_name = 'htmx/bulk_add_form.html' def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'add') + def get_pattern_context(self, value): + """ + Return a context mapping for substituting the generated pattern value into + model form fields. + + By default, the field named by ``pattern_target`` is supported as a + placeholder, e.g. ``{vid}``. + """ + if not self.pattern_target: + return {} + + return { + self.pattern_target: str(value), + } + + def render_pattern_template(self, template, value): + """ + Replace pattern placeholders in a single form field value. + """ + rendered = str(template) + + for key, replacement in self.get_pattern_context(value).items(): + rendered = rendered.replace(f'{{{key}}}', replacement) + + return rendered + + def apply_pattern_template_fields(self, data, value): + """ + Apply the generated pattern value to any configured template fields. + """ + for field_name in self.pattern_template_fields: + if field_name not in data: + continue + + # QueryDict values may be multi-valued; preserve that behavior. + if hasattr(data, 'getlist') and hasattr(data, 'setlist'): + data.setlist(field_name, [ + self.render_pattern_template(field_value, value) + for field_value in data.getlist(field_name) + ]) + else: + data[field_name] = self.render_pattern_template(data[field_name], value) + + return data + + def get_model_form_data(self, form, request, value): + """ + Return the submitted data to use when instantiating the model form for a + single generated pattern value. + """ + data = request.POST.copy() + data[self.pattern_target] = value + + return self.apply_pattern_template_fields(data, value) + + def add_model_form_errors(self, form, model_form, value): + """ + Copy validation errors from the generated object's model form back onto + the pattern form for display. + """ + errors = model_form.errors.as_data() + + if errors.get(self.pattern_target): + form.add_error('pattern', errors.pop(self.pattern_target)) + + for field_name, field_errors in errors.items(): + if field_name == '__all__': + field_label = _('General') + elif field_name in model_form.fields: + field_label = model_form.fields[field_name].label + else: + field_label = field_name + + for error in field_errors: + for message in error.messages: + form.add_error( + None, + _('{value}: {field}: {error}').format( + value=value, + field=field_label, + error=message, + ) + ) + def _create_objects(self, form, request): new_objects = [] @@ -287,8 +372,7 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): # Reinstantiate the model form each time to avoid overwriting the same instance. Use a mutable # copy of the POST QueryDict so that we can update the target field value. - model_form = self.model_form(request.POST.copy()) - model_form.data[self.pattern_target] = value + model_form = self.model_form(self.get_model_form_data(form, request, value)) # Validate each new object independently. if model_form.is_valid(): @@ -296,12 +380,10 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): obj = model_form.save() new_objects.append(obj) else: - # Copy any errors on the pattern target field to the pattern form. - errors = model_form.errors.as_data() - if errors.get(self.pattern_target): - form.add_error('pattern', errors[self.pattern_target]) - # Raise an IntegrityError to break the for loop and abort the transaction. - raise IntegrityError() + self.add_model_form_errors(form, model_form, value) + + # Abort the transaction and break out of the loop. + raise AbortTransaction() return new_objects @@ -372,7 +454,7 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): return redirect(request.path) return redirect(self.get_return_url(request)) - except IntegrityError: + except (AbortTransaction, IntegrityError): pass except (AbortRequest, PermissionsViolation) as e: @@ -758,7 +840,10 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): # Update custom fields for name, customfield in custom_fields.items(): - assert name.startswith('cf_') + if not name.startswith('cf_'): + raise ImproperlyConfigured( + _("Custom field form field name must begin with 'cf_': {name}").format(name=name) + ) cf_name = name[3:] # Strip cf_ prefix if name in form.nullable_fields and name in nullified_fields: obj.custom_field_data[cf_name] = None diff --git a/netbox/netbox/views/misc.py b/netbox/netbox/views/misc.py index 29833fba9..2eeba7cb8 100644 --- a/netbox/netbox/views/misc.py +++ b/netbox/netbox/views/misc.py @@ -1,4 +1,5 @@ import logging +import posixpath import re from collections import namedtuple @@ -6,6 +7,8 @@ from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.core.cache import cache +from django.db.models import Q +from django.http import Http404 from django.shortcuts import redirect, render from django.utils.translation import gettext_lazy as _ from django.views.generic import View @@ -13,8 +16,10 @@ from django.views.static import serve from django_tables2 import RequestConfig from packaging import version +from dcim.models import DeviceType from extras.constants import DEFAULT_DASHBOARD from extras.dashboard.utils import get_dashboard, get_default_dashboard +from extras.models import ImageAttachment from netbox.forms import SearchForm from netbox.search import LookupTypes from netbox.search.backends import search_backend @@ -131,7 +136,29 @@ class SearchView(ConditionalLoginRequiredMixin, View): class MediaView(TokenConditionalLoginRequiredMixin, View): """ - Wrap Django's serve() view to enforce LOGIN_REQUIRED for static media. + Serve uploaded media files, enforcing authentication and view permission on the associated object. """ def get(self, request, path): - return serve(request, path, document_root=settings.MEDIA_ROOT) + + # Normalize the path to prevent traversal sequences (e.g. "foo/../image-attachments/...") + # from bypassing the directory checks below. + path = posixpath.normpath(path).lstrip('/') + + # For known upload directories, resolve the path to an owning record and + # enforce object-level view permission. restrict() returns .none() when the + # user lacks permission, so a denial and a missing file are both 404s. + # Paths outside these directories (e.g. plugin uploads) fall through + # to the original behaviour. + if path.startswith('image-attachments/'): + if not ImageAttachment.objects.restrict(request.user, 'view').filter(image=path).exists(): + raise Http404 + elif path.startswith('devicetype-images/'): + if not DeviceType.objects.restrict(request.user, 'view').filter( + Q(front_image=path) | Q(rear_image=path) + ).exists(): + raise Http404 + + response = serve(request, path, document_root=settings.MEDIA_ROOT) + response['Content-Disposition'] = 'attachment' + response['X-Content-Type-Options'] = 'nosniff' + return response diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 001c84bb1..05b3de92d 100644 --- a/netbox/project-static/dist/netbox.js +++ b/netbox/project-static/dist/netbox.js @@ -1,4 +1,4 @@ -"use strict";(()=>{var iu=Object.create;var Di=Object.defineProperty,ru=Object.defineProperties,ou=Object.getOwnPropertyDescriptor,su=Object.getOwnPropertyDescriptors,au=Object.getOwnPropertyNames,ps=Object.getOwnPropertySymbols,lu=Object.getPrototypeOf,ms=Object.prototype.hasOwnProperty,cu=Object.prototype.propertyIsEnumerable;var Wr=(n,e,t)=>e in n?Di(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,D=(n,e)=>{for(var t in e||(e={}))ms.call(e,t)&&Wr(n,t,e[t]);if(ps)for(var t of ps(e))cu.call(e,t)&&Wr(n,t,e[t]);return n},se=(n,e)=>ru(n,su(e));var uu=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports),gs=(n,e)=>{for(var t in e)Di(n,t,{get:e[t],enumerable:!0})},du=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of au(e))!ms.call(n,r)&&r!==t&&Di(n,r,{get:()=>e[r],enumerable:!(i=ou(e,r))||i.enumerable});return n};var fu=(n,e,t)=>(t=n!=null?iu(lu(n)):{},du(e||!n||!n.__esModule?Di(t,"default",{value:n,enumerable:!0}):t,n));var le=(n,e,t)=>Wr(n,typeof e!="symbol"?e+"":e,t);var at=(n,e,t)=>new Promise((i,r)=>{var o=l=>{try{a(t.next(l))}catch(c){r(c)}},s=l=>{try{a(t.throw(l))}catch(c){r(c)}},a=l=>l.done?i(l.value):Promise.resolve(l.value).then(o,s);a((t=t.apply(n,e)).next())});var bc=uu((pi,ns)=>{(function(e,t){typeof pi=="object"&&typeof ns=="object"?ns.exports=t():typeof define=="function"&&define.amd?define([],t):typeof pi=="object"?pi.ClipboardJS=t():e.ClipboardJS=t()})(pi,function(){return(function(){var n={686:(function(i,r,o){"use strict";o.d(r,{default:function(){return Re}});var s=o(279),a=o.n(s),l=o(370),c=o.n(l),u=o(817),d=o.n(u);function p(q){try{return document.execCommand(q)}catch(M){return!1}}var y=function(M){var A=d()(M);return p("cut"),A},m=y;function g(q){var M=document.documentElement.getAttribute("dir")==="rtl",A=document.createElement("textarea");A.style.fontSize="12pt",A.style.border="0",A.style.padding="0",A.style.margin="0",A.style.position="absolute",A.style[M?"right":"left"]="-9999px";var B=window.pageYOffset||document.documentElement.scrollTop;return A.style.top="".concat(B,"px"),A.setAttribute("readonly",""),A.value=q,A}var _=function(M,A){var B=g(M);A.container.appendChild(B);var V=d()(B);return p("copy"),B.remove(),V},C=function(M){var A=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},B="";return typeof M=="string"?B=_(M,A):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?B=_(M.value,A):(B=d()(M),p("copy")),B},O=C;function w(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?w=function(A){return typeof A}:w=function(A){return A&&typeof Symbol=="function"&&A.constructor===Symbol&&A!==Symbol.prototype?"symbol":typeof A},w(q)}var T=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},A=M.action,B=A===void 0?"copy":A,V=M.container,U=M.target,Y=M.text;if(B!=="copy"&&B!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(U!==void 0)if(U&&w(U)==="object"&&U.nodeType===1){if(B==="copy"&&U.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(B==="cut"&&(U.hasAttribute("readonly")||U.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Y)return O(Y,{container:V});if(U)return B==="cut"?m(U):O(U,{container:V})},$=T;function j(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?j=function(A){return typeof A}:j=function(A){return A&&typeof Symbol=="function"&&A.constructor===Symbol&&A!==Symbol.prototype?"symbol":typeof A},j(q)}function H(q,M){if(!(q instanceof M))throw new TypeError("Cannot call a class as a function")}function I(q,M){for(var A=0;A0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof V.action=="function"?V.action:this.defaultAction,this.target=typeof V.target=="function"?V.target:this.defaultTarget,this.text=typeof V.text=="function"?V.text:this.defaultText,this.container=j(V.container)==="object"?V.container:document.body}},{key:"listenClick",value:function(V){var U=this;this.listener=c()(V,"click",function(Y){return U.onClick(Y)})}},{key:"onClick",value:function(V){var U=V.delegateTarget||V.currentTarget,Y=this.action(U)||"copy",ee=$({action:Y,container:this.container,target:this.target(U),text:this.text(U)});this.emit(ee?"success":"error",{action:Y,text:ee,trigger:U,clearSelection:function(){U&&U.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(V){return ne("action",V)}},{key:"defaultTarget",value:function(V){var U=ne("target",V);if(U)return document.querySelector(U)}},{key:"defaultText",value:function(V){return ne("text",V)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(V){var U=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return O(V,U)}},{key:"cut",value:function(V){return m(V)}},{key:"isSupported",value:function(){var V=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],U=typeof V=="string"?[V]:V,Y=!!document.queryCommandSupported;return U.forEach(function(ee){Y=Y&&!!document.queryCommandSupported(ee)}),Y}}]),A})(a()),Re=Ue}),828:(function(i){var r=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var o=Element.prototype;o.matches=o.matchesSelector||o.mozMatchesSelector||o.msMatchesSelector||o.oMatchesSelector||o.webkitMatchesSelector}function s(a,l){for(;a&&a.nodeType!==r;){if(typeof a.matches=="function"&&a.matches(l))return a;a=a.parentNode}}i.exports=s}),438:(function(i,r,o){var s=o(828);function a(u,d,p,y,m){var g=c.apply(this,arguments);return u.addEventListener(p,g,m),{destroy:function(){u.removeEventListener(p,g,m)}}}function l(u,d,p,y,m){return typeof u.addEventListener=="function"?a.apply(null,arguments):typeof p=="function"?a.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(g){return a(g,d,p,y,m)}))}function c(u,d,p,y){return function(m){m.delegateTarget=s(m.target,d),m.delegateTarget&&y.call(u,m)}}i.exports=l}),879:(function(i,r){r.node=function(o){return o!==void 0&&o instanceof HTMLElement&&o.nodeType===1},r.nodeList=function(o){var s=Object.prototype.toString.call(o);return o!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in o&&(o.length===0||r.node(o[0]))},r.string=function(o){return typeof o=="string"||o instanceof String},r.fn=function(o){var s=Object.prototype.toString.call(o);return s==="[object Function]"}}),370:(function(i,r,o){var s=o(879),a=o(438);function l(p,y,m){if(!p&&!y&&!m)throw new Error("Missing required arguments");if(!s.string(y))throw new TypeError("Second argument must be a String");if(!s.fn(m))throw new TypeError("Third argument must be a Function");if(s.node(p))return c(p,y,m);if(s.nodeList(p))return u(p,y,m);if(s.string(p))return d(p,y,m);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(p,y,m){return p.addEventListener(y,m),{destroy:function(){p.removeEventListener(y,m)}}}function u(p,y,m){return Array.prototype.forEach.call(p,function(g){g.addEventListener(y,m)}),{destroy:function(){Array.prototype.forEach.call(p,function(g){g.removeEventListener(y,m)})}}}function d(p,y,m){return a(document.body,p,y,m)}i.exports=l}),817:(function(i){function r(o){var s;if(o.nodeName==="SELECT")o.focus(),s=o.value;else if(o.nodeName==="INPUT"||o.nodeName==="TEXTAREA"){var a=o.hasAttribute("readonly");a||o.setAttribute("readonly",""),o.select(),o.setSelectionRange(0,o.value.length),a||o.removeAttribute("readonly"),s=o.value}else{o.hasAttribute("contenteditable")&&o.focus();var l=window.getSelection(),c=document.createRange();c.selectNodeContents(o),l.removeAllRanges(),l.addRange(c),s=l.toString()}return s}i.exports=r}),279:(function(i){function r(){}r.prototype={on:function(o,s,a){var l=this.e||(this.e={});return(l[o]||(l[o]=[])).push({fn:s,ctx:a}),this},once:function(o,s,a){var l=this;function c(){l.off(o,c),s.apply(a,arguments)}return c._=s,this.on(o,c,a)},emit:function(o){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[o]||[]).slice(),l=0,c=a.length;for(l;lws,afterRead:()=>Es,afterWrite:()=>Cs,applyStyles:()=>fn,arrow:()=>Li,auto:()=>Bn,basePlacements:()=>lt,beforeMain:()=>bs,beforeRead:()=>vs,beforeWrite:()=>xs,bottom:()=>ge,clippingParents:()=>qr,computeStyles:()=>pn,createPopper:()=>Kn,createPopperBase:()=>Hs,createPopperLite:()=>Is,detectOverflow:()=>ke,end:()=>bt,eventListeners:()=>mn,flip:()=>Hi,hide:()=>Ii,left:()=>pe,main:()=>_s,modifierPhases:()=>Yr,offset:()=>Ri,placements:()=>zn,popper:()=>$t,popperGenerator:()=>Yt,popperOffsets:()=>yn,preventOverflow:()=>Pi,read:()=>ys,reference:()=>Ur,right:()=>me,start:()=>rt,top:()=>de,variationPlacements:()=>Oi,viewport:()=>Vn,write:()=>Ts});var de="top",ge="bottom",me="right",pe="left",Bn="auto",lt=[de,ge,me,pe],rt="start",bt="end",qr="clippingParents",Vn="viewport",$t="popper",Ur="reference",Oi=lt.reduce(function(n,e){return n.concat([e+"-"+rt,e+"-"+bt])},[]),zn=[].concat(lt,[Bn]).reduce(function(n,e){return n.concat([e,e+"-"+rt,e+"-"+bt])},[]),vs="beforeRead",ys="read",Es="afterRead",bs="beforeMain",_s="main",ws="afterMain",xs="beforeWrite",Ts="write",Cs="afterWrite",Yr=[vs,ys,Es,bs,_s,ws,xs,Ts,Cs];function xe(n){return n?(n.nodeName||"").toLowerCase():null}function ce(n){if(n==null)return window;if(n.toString()!=="[object Window]"){var e=n.ownerDocument;return e&&e.defaultView||window}return n}function Ye(n){var e=ce(n).Element;return n instanceof e||n instanceof Element}function _e(n){var e=ce(n).HTMLElement;return n instanceof e||n instanceof HTMLElement}function dn(n){if(typeof ShadowRoot=="undefined")return!1;var e=ce(n).ShadowRoot;return n instanceof e||n instanceof ShadowRoot}function hu(n){var e=n.state;Object.keys(e.elements).forEach(function(t){var i=e.styles[t]||{},r=e.attributes[t]||{},o=e.elements[t];!_e(o)||!xe(o)||(Object.assign(o.style,i),Object.keys(r).forEach(function(s){var a=r[s];a===!1?o.removeAttribute(s):o.setAttribute(s,a===!0?"":a)}))})}function pu(n){var e=n.state,t={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,t.popper),e.styles=t,e.elements.arrow&&Object.assign(e.elements.arrow.style,t.arrow),function(){Object.keys(e.elements).forEach(function(i){var r=e.elements[i],o=e.attributes[i]||{},s=Object.keys(e.styles.hasOwnProperty(i)?e.styles[i]:t[i]),a=s.reduce(function(l,c){return l[c]="",l},{});!_e(r)||!xe(r)||(Object.assign(r.style,a),Object.keys(o).forEach(function(l){r.removeAttribute(l)}))})}}var fn={name:"applyStyles",enabled:!0,phase:"write",fn:hu,effect:pu,requires:["computeStyles"]};function Te(n){return n.split("-")[0]}var Ze=Math.max,Bt=Math.min,ct=Math.round;function hn(){var n=navigator.userAgentData;return n!=null&&n.brands&&Array.isArray(n.brands)?n.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function jn(){return!/^((?!chrome|android).)*safari/i.test(hn())}function Ge(n,e,t){e===void 0&&(e=!1),t===void 0&&(t=!1);var i=n.getBoundingClientRect(),r=1,o=1;e&&_e(n)&&(r=n.offsetWidth>0&&ct(i.width)/n.offsetWidth||1,o=n.offsetHeight>0&&ct(i.height)/n.offsetHeight||1);var s=Ye(n)?ce(n):window,a=s.visualViewport,l=!jn()&&t,c=(i.left+(l&&a?a.offsetLeft:0))/r,u=(i.top+(l&&a?a.offsetTop:0))/o,d=i.width/r,p=i.height/o;return{width:d,height:p,top:u,right:c+d,bottom:u+p,left:c,x:c,y:u}}function Vt(n){var e=Ge(n),t=n.offsetWidth,i=n.offsetHeight;return Math.abs(e.width-t)<=1&&(t=e.width),Math.abs(e.height-i)<=1&&(i=e.height),{x:n.offsetLeft,y:n.offsetTop,width:t,height:i}}function Wn(n,e){var t=e.getRootNode&&e.getRootNode();if(n.contains(e))return!0;if(t&&dn(t)){var i=e;do{if(i&&n.isSameNode(i))return!0;i=i.parentNode||i.host}while(i)}return!1}function Ne(n){return ce(n).getComputedStyle(n)}function Gr(n){return["table","td","th"].indexOf(xe(n))>=0}function Se(n){return((Ye(n)?n.ownerDocument:n.document)||window.document).documentElement}function ut(n){return xe(n)==="html"?n:n.assignedSlot||n.parentNode||(dn(n)?n.host:null)||Se(n)}function Ss(n){return!_e(n)||Ne(n).position==="fixed"?null:n.offsetParent}function mu(n){var e=/firefox/i.test(hn()),t=/Trident/i.test(hn());if(t&&_e(n)){var i=Ne(n);if(i.position==="fixed")return null}var r=ut(n);for(dn(r)&&(r=r.host);_e(r)&&["html","body"].indexOf(xe(r))<0;){var o=Ne(r);if(o.transform!=="none"||o.perspective!=="none"||o.contain==="paint"||["transform","perspective"].indexOf(o.willChange)!==-1||e&&o.willChange==="filter"||e&&o.filter&&o.filter!=="none")return r;r=r.parentNode}return null}function et(n){for(var e=ce(n),t=Ss(n);t&&Gr(t)&&Ne(t).position==="static";)t=Ss(t);return t&&(xe(t)==="html"||xe(t)==="body"&&Ne(t).position==="static")?e:t||mu(n)||e}function zt(n){return["top","bottom"].indexOf(n)>=0?"x":"y"}function jt(n,e,t){return Ze(n,Bt(e,t))}function As(n,e,t){var i=jt(n,e,t);return i>t?t:i}function qn(){return{top:0,right:0,bottom:0,left:0}}function Un(n){return Object.assign({},qn(),n)}function Yn(n,e){return e.reduce(function(t,i){return t[i]=n,t},{})}var gu=function(e,t){return e=typeof e=="function"?e(Object.assign({},t.rects,{placement:t.placement})):e,Un(typeof e!="number"?e:Yn(e,lt))};function vu(n){var e,t=n.state,i=n.name,r=n.options,o=t.elements.arrow,s=t.modifiersData.popperOffsets,a=Te(t.placement),l=zt(a),c=[pe,me].indexOf(a)>=0,u=c?"height":"width";if(!(!o||!s)){var d=gu(r.padding,t),p=Vt(o),y=l==="y"?de:pe,m=l==="y"?ge:me,g=t.rects.reference[u]+t.rects.reference[l]-s[l]-t.rects.popper[u],_=s[l]-t.rects.reference[l],C=et(o),O=C?l==="y"?C.clientHeight||0:C.clientWidth||0:0,w=g/2-_/2,T=d[y],$=O-p[u]-d[m],j=O/2-p[u]/2+w,H=jt(T,j,$),I=l;t.modifiersData[i]=(e={},e[I]=H,e.centerOffset=H-j,e)}}function yu(n){var e=n.state,t=n.options,i=t.element,r=i===void 0?"[data-popper-arrow]":i;r!=null&&(typeof r=="string"&&(r=e.elements.popper.querySelector(r),!r)||Wn(e.elements.popper,r)&&(e.elements.arrow=r))}var Li={name:"arrow",enabled:!0,phase:"main",fn:vu,effect:yu,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Ke(n){return n.split("-")[1]}var Eu={top:"auto",right:"auto",bottom:"auto",left:"auto"};function bu(n,e){var t=n.x,i=n.y,r=e.devicePixelRatio||1;return{x:ct(t*r)/r||0,y:ct(i*r)/r||0}}function Ds(n){var e,t=n.popper,i=n.popperRect,r=n.placement,o=n.variation,s=n.offsets,a=n.position,l=n.gpuAcceleration,c=n.adaptive,u=n.roundOffsets,d=n.isFixed,p=s.x,y=p===void 0?0:p,m=s.y,g=m===void 0?0:m,_=typeof u=="function"?u({x:y,y:g}):{x:y,y:g};y=_.x,g=_.y;var C=s.hasOwnProperty("x"),O=s.hasOwnProperty("y"),w=pe,T=de,$=window;if(c){var j=et(t),H="clientHeight",I="clientWidth";if(j===ce(t)&&(j=Se(t),Ne(j).position!=="static"&&a==="absolute"&&(H="scrollHeight",I="scrollWidth")),j=j,r===de||(r===pe||r===me)&&o===bt){T=ge;var L=d&&j===$&&$.visualViewport?$.visualViewport.height:j[H];g-=L-i.height,g*=l?1:-1}if(r===pe||(r===de||r===ge)&&o===bt){w=me;var W=d&&j===$&&$.visualViewport?$.visualViewport.width:j[I];y-=W-i.width,y*=l?1:-1}}var G=Object.assign({position:a},c&&Eu),Q=u===!0?bu({x:y,y:g},ce(t)):{x:y,y:g};if(y=Q.x,g=Q.y,l){var Z;return Object.assign({},G,(Z={},Z[T]=O?"0":"",Z[w]=C?"0":"",Z.transform=($.devicePixelRatio||1)<=1?"translate("+y+"px, "+g+"px)":"translate3d("+y+"px, "+g+"px, 0)",Z))}return Object.assign({},G,(e={},e[T]=O?g+"px":"",e[w]=C?y+"px":"",e.transform="",e))}function _u(n){var e=n.state,t=n.options,i=t.gpuAcceleration,r=i===void 0?!0:i,o=t.adaptive,s=o===void 0?!0:o,a=t.roundOffsets,l=a===void 0?!0:a,c={placement:Te(e.placement),variation:Ke(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:r,isFixed:e.options.strategy==="fixed"};e.modifiersData.popperOffsets!=null&&(e.styles.popper=Object.assign({},e.styles.popper,Ds(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:s,roundOffsets:l})))),e.modifiersData.arrow!=null&&(e.styles.arrow=Object.assign({},e.styles.arrow,Ds(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})}var pn={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:_u,data:{}};var Mi={passive:!0};function wu(n){var e=n.state,t=n.instance,i=n.options,r=i.scroll,o=r===void 0?!0:r,s=i.resize,a=s===void 0?!0:s,l=ce(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach(function(u){u.addEventListener("scroll",t.update,Mi)}),a&&l.addEventListener("resize",t.update,Mi),function(){o&&c.forEach(function(u){u.removeEventListener("scroll",t.update,Mi)}),a&&l.removeEventListener("resize",t.update,Mi)}}var mn={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:wu,data:{}};var xu={left:"right",right:"left",bottom:"top",top:"bottom"};function gn(n){return n.replace(/left|right|bottom|top/g,function(e){return xu[e]})}var Tu={start:"end",end:"start"};function Ni(n){return n.replace(/start|end/g,function(e){return Tu[e]})}function Wt(n){var e=ce(n),t=e.pageXOffset,i=e.pageYOffset;return{scrollLeft:t,scrollTop:i}}function qt(n){return Ge(Se(n)).left+Wt(n).scrollLeft}function Kr(n,e){var t=ce(n),i=Se(n),r=t.visualViewport,o=i.clientWidth,s=i.clientHeight,a=0,l=0;if(r){o=r.width,s=r.height;var c=jn();(c||!c&&e==="fixed")&&(a=r.offsetLeft,l=r.offsetTop)}return{width:o,height:s,x:a+qt(n),y:l}}function Xr(n){var e,t=Se(n),i=Wt(n),r=(e=n.ownerDocument)==null?void 0:e.body,o=Ze(t.scrollWidth,t.clientWidth,r?r.scrollWidth:0,r?r.clientWidth:0),s=Ze(t.scrollHeight,t.clientHeight,r?r.scrollHeight:0,r?r.clientHeight:0),a=-i.scrollLeft+qt(n),l=-i.scrollTop;return Ne(r||t).direction==="rtl"&&(a+=Ze(t.clientWidth,r?r.clientWidth:0)-o),{width:o,height:s,x:a,y:l}}function Ut(n){var e=Ne(n),t=e.overflow,i=e.overflowX,r=e.overflowY;return/auto|scroll|overlay|hidden/.test(t+r+i)}function ki(n){return["html","body","#document"].indexOf(xe(n))>=0?n.ownerDocument.body:_e(n)&&Ut(n)?n:ki(ut(n))}function _t(n,e){var t;e===void 0&&(e=[]);var i=ki(n),r=i===((t=n.ownerDocument)==null?void 0:t.body),o=ce(i),s=r?[o].concat(o.visualViewport||[],Ut(i)?i:[]):i,a=e.concat(s);return r?a:a.concat(_t(ut(s)))}function vn(n){return Object.assign({},n,{left:n.x,top:n.y,right:n.x+n.width,bottom:n.y+n.height})}function Cu(n,e){var t=Ge(n,!1,e==="fixed");return t.top=t.top+n.clientTop,t.left=t.left+n.clientLeft,t.bottom=t.top+n.clientHeight,t.right=t.left+n.clientWidth,t.width=n.clientWidth,t.height=n.clientHeight,t.x=t.left,t.y=t.top,t}function Os(n,e,t){return e===Vn?vn(Kr(n,t)):Ye(e)?Cu(e,t):vn(Xr(Se(n)))}function Su(n){var e=_t(ut(n)),t=["absolute","fixed"].indexOf(Ne(n).position)>=0,i=t&&_e(n)?et(n):n;return Ye(i)?e.filter(function(r){return Ye(r)&&Wn(r,i)&&xe(r)!=="body"}):[]}function Qr(n,e,t,i){var r=e==="clippingParents"?Su(n):[].concat(e),o=[].concat(r,[t]),s=o[0],a=o.reduce(function(l,c){var u=Os(n,c,i);return l.top=Ze(u.top,l.top),l.right=Bt(u.right,l.right),l.bottom=Bt(u.bottom,l.bottom),l.left=Ze(u.left,l.left),l},Os(n,s,i));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}function Gn(n){var e=n.reference,t=n.element,i=n.placement,r=i?Te(i):null,o=i?Ke(i):null,s=e.x+e.width/2-t.width/2,a=e.y+e.height/2-t.height/2,l;switch(r){case de:l={x:s,y:e.y-t.height};break;case ge:l={x:s,y:e.y+e.height};break;case me:l={x:e.x+e.width,y:a};break;case pe:l={x:e.x-t.width,y:a};break;default:l={x:e.x,y:e.y}}var c=r?zt(r):null;if(c!=null){var u=c==="y"?"height":"width";switch(o){case rt:l[c]=l[c]-(e[u]/2-t[u]/2);break;case bt:l[c]=l[c]+(e[u]/2-t[u]/2);break;default:}}return l}function ke(n,e){e===void 0&&(e={});var t=e,i=t.placement,r=i===void 0?n.placement:i,o=t.strategy,s=o===void 0?n.strategy:o,a=t.boundary,l=a===void 0?qr:a,c=t.rootBoundary,u=c===void 0?Vn:c,d=t.elementContext,p=d===void 0?$t:d,y=t.altBoundary,m=y===void 0?!1:y,g=t.padding,_=g===void 0?0:g,C=Un(typeof _!="number"?_:Yn(_,lt)),O=p===$t?Ur:$t,w=n.rects.popper,T=n.elements[m?O:p],$=Qr(Ye(T)?T:T.contextElement||Se(n.elements.popper),l,u,s),j=Ge(n.elements.reference),H=Gn({reference:j,element:w,strategy:"absolute",placement:r}),I=vn(Object.assign({},w,H)),L=p===$t?I:j,W={top:$.top-L.top+C.top,bottom:L.bottom-$.bottom+C.bottom,left:$.left-L.left+C.left,right:L.right-$.right+C.right},G=n.modifiersData.offset;if(p===$t&&G){var Q=G[r];Object.keys(W).forEach(function(Z){var he=[me,ge].indexOf(Z)>=0?1:-1,Ce=[de,ge].indexOf(Z)>=0?"y":"x";W[Z]+=Q[Ce]*he})}return W}function Jr(n,e){e===void 0&&(e={});var t=e,i=t.placement,r=t.boundary,o=t.rootBoundary,s=t.padding,a=t.flipVariations,l=t.allowedAutoPlacements,c=l===void 0?zn:l,u=Ke(i),d=u?a?Oi:Oi.filter(function(m){return Ke(m)===u}):lt,p=d.filter(function(m){return c.indexOf(m)>=0});p.length===0&&(p=d);var y=p.reduce(function(m,g){return m[g]=ke(n,{placement:g,boundary:r,rootBoundary:o,padding:s})[Te(g)],m},{});return Object.keys(y).sort(function(m,g){return y[m]-y[g]})}function Au(n){if(Te(n)===Bn)return[];var e=gn(n);return[Ni(n),e,Ni(e)]}function Du(n){var e=n.state,t=n.options,i=n.name;if(!e.modifiersData[i]._skip){for(var r=t.mainAxis,o=r===void 0?!0:r,s=t.altAxis,a=s===void 0?!0:s,l=t.fallbackPlacements,c=t.padding,u=t.boundary,d=t.rootBoundary,p=t.altBoundary,y=t.flipVariations,m=y===void 0?!0:y,g=t.allowedAutoPlacements,_=e.options.placement,C=Te(_),O=C===_,w=l||(O||!m?[gn(_)]:Au(_)),T=[_].concat(w).reduce(function(V,U){return V.concat(Te(U)===Bn?Jr(e,{placement:U,boundary:u,rootBoundary:d,padding:c,flipVariations:m,allowedAutoPlacements:g}):U)},[]),$=e.rects.reference,j=e.rects.popper,H=new Map,I=!0,L=T[0],W=0;W=0,Ce=he?"width":"height",ie=ke(e,{placement:G,boundary:u,rootBoundary:d,altBoundary:p,padding:c}),ne=he?Z?me:pe:Z?ge:de;$[Ce]>j[Ce]&&(ne=gn(ne));var Ue=gn(ne),Re=[];if(o&&Re.push(ie[Q]<=0),a&&Re.push(ie[ne]<=0,ie[Ue]<=0),Re.every(function(V){return V})){L=G,I=!1;break}H.set(G,Re)}if(I)for(var q=m?3:1,M=function(U){var Y=T.find(function(ee){var oe=H.get(ee);if(oe)return oe.slice(0,U).every(function(Et){return Et})});if(Y)return L=Y,"break"},A=q;A>0;A--){var B=M(A);if(B==="break")break}e.placement!==L&&(e.modifiersData[i]._skip=!0,e.placement=L,e.reset=!0)}}var Hi={name:"flip",enabled:!0,phase:"main",fn:Du,requiresIfExists:["offset"],data:{_skip:!1}};function Ls(n,e,t){return t===void 0&&(t={x:0,y:0}),{top:n.top-e.height-t.y,right:n.right-e.width+t.x,bottom:n.bottom-e.height+t.y,left:n.left-e.width-t.x}}function Ms(n){return[de,me,ge,pe].some(function(e){return n[e]>=0})}function Ou(n){var e=n.state,t=n.name,i=e.rects.reference,r=e.rects.popper,o=e.modifiersData.preventOverflow,s=ke(e,{elementContext:"reference"}),a=ke(e,{altBoundary:!0}),l=Ls(s,i),c=Ls(a,r,o),u=Ms(l),d=Ms(c);e.modifiersData[t]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:u,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":u,"data-popper-escaped":d})}var Ii={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:Ou};function Lu(n,e,t){var i=Te(n),r=[pe,de].indexOf(i)>=0?-1:1,o=typeof t=="function"?t(Object.assign({},e,{placement:n})):t,s=o[0],a=o[1];return s=s||0,a=(a||0)*r,[pe,me].indexOf(i)>=0?{x:a,y:s}:{x:s,y:a}}function Mu(n){var e=n.state,t=n.options,i=n.name,r=t.offset,o=r===void 0?[0,0]:r,s=zn.reduce(function(u,d){return u[d]=Lu(d,e.rects,o),u},{}),a=s[e.placement],l=a.x,c=a.y;e.modifiersData.popperOffsets!=null&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[i]=s}var Ri={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Mu};function Nu(n){var e=n.state,t=n.name;e.modifiersData[t]=Gn({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})}var yn={name:"popperOffsets",enabled:!0,phase:"read",fn:Nu,data:{}};function Zr(n){return n==="x"?"y":"x"}function ku(n){var e=n.state,t=n.options,i=n.name,r=t.mainAxis,o=r===void 0?!0:r,s=t.altAxis,a=s===void 0?!1:s,l=t.boundary,c=t.rootBoundary,u=t.altBoundary,d=t.padding,p=t.tether,y=p===void 0?!0:p,m=t.tetherOffset,g=m===void 0?0:m,_=ke(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:u}),C=Te(e.placement),O=Ke(e.placement),w=!O,T=zt(C),$=Zr(T),j=e.modifiersData.popperOffsets,H=e.rects.reference,I=e.rects.popper,L=typeof g=="function"?g(Object.assign({},e.rects,{placement:e.placement})):g,W=typeof L=="number"?{mainAxis:L,altAxis:L}:Object.assign({mainAxis:0,altAxis:0},L),G=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,Q={x:0,y:0};if(j){if(o){var Z,he=T==="y"?de:pe,Ce=T==="y"?ge:me,ie=T==="y"?"height":"width",ne=j[T],Ue=ne+_[he],Re=ne-_[Ce],q=y?-I[ie]/2:0,M=O===rt?H[ie]:I[ie],A=O===rt?-I[ie]:-H[ie],B=e.elements.arrow,V=y&&B?Vt(B):{width:0,height:0},U=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:qn(),Y=U[he],ee=U[Ce],oe=jt(0,H[ie],V[ie]),Et=w?H[ie]/2-q-oe-Y-W.mainAxis:M-oe-Y-W.mainAxis,Fr=w?-H[ie]/2+q+oe+ee+W.mainAxis:A+oe+ee+W.mainAxis,rn=e.elements.arrow&&et(e.elements.arrow),on=rn?T==="y"?rn.clientTop||0:rn.clientLeft||0:0,_i=(Z=G==null?void 0:G[T])!=null?Z:0,$r=ne+Et-_i-on,wi=ne+Fr-_i,xi=jt(y?Bt(Ue,$r):Ue,ne,y?Ze(Re,wi):Re);j[T]=xi,Q[T]=xi-ne}if(a){var Rn,Ti=T==="x"?de:pe,sn=T==="x"?ge:me,ot=j[$],an=$==="y"?"height":"width",Pn=ot+_[Ti],ln=ot-_[sn],cn=[de,pe].indexOf(C)!==-1,Ft=(Rn=G==null?void 0:G[$])!=null?Rn:0,Ci=cn?Pn:ot-H[an]-I[an]-Ft+W.altAxis,Fn=cn?ot+H[an]+I[an]-Ft-W.altAxis:ln,Si=y&&cn?As(Ci,ot,Fn):jt(y?Ci:Pn,ot,y?Fn:ln);j[$]=Si,Q[$]=Si-ot}e.modifiersData[i]=Q}}var Pi={name:"preventOverflow",enabled:!0,phase:"main",fn:ku,requiresIfExists:["offset"]};function eo(n){return{scrollLeft:n.scrollLeft,scrollTop:n.scrollTop}}function to(n){return n===ce(n)||!_e(n)?Wt(n):eo(n)}function Hu(n){var e=n.getBoundingClientRect(),t=ct(e.width)/n.offsetWidth||1,i=ct(e.height)/n.offsetHeight||1;return t!==1||i!==1}function no(n,e,t){t===void 0&&(t=!1);var i=_e(e),r=_e(e)&&Hu(e),o=Se(e),s=Ge(n,r,t),a={scrollLeft:0,scrollTop:0},l={x:0,y:0};return(i||!i&&!t)&&((xe(e)!=="body"||Ut(o))&&(a=to(e)),_e(e)?(l=Ge(e,!0),l.x+=e.clientLeft,l.y+=e.clientTop):o&&(l.x=qt(o))),{x:s.left+a.scrollLeft-l.x,y:s.top+a.scrollTop-l.y,width:s.width,height:s.height}}function Iu(n){var e=new Map,t=new Set,i=[];n.forEach(function(o){e.set(o.name,o)});function r(o){t.add(o.name);var s=[].concat(o.requires||[],o.requiresIfExists||[]);s.forEach(function(a){if(!t.has(a)){var l=e.get(a);l&&r(l)}}),i.push(o)}return n.forEach(function(o){t.has(o.name)||r(o)}),i}function io(n){var e=Iu(n);return Yr.reduce(function(t,i){return t.concat(e.filter(function(r){return r.phase===i}))},[])}function ro(n){var e;return function(){return e||(e=new Promise(function(t){Promise.resolve().then(function(){e=void 0,t(n())})})),e}}function oo(n){var e=n.reduce(function(t,i){var r=t[i.name];return t[i.name]=r?Object.assign({},r,i,{options:Object.assign({},r.options,i.options),data:Object.assign({},r.data,i.data)}):i,t},{});return Object.keys(e).map(function(t){return e[t]})}var Ns={placement:"bottom",modifiers:[],strategy:"absolute"};function ks(){for(var n=arguments.length,e=new Array(n),t=0;t(n&&window.CSS&&window.CSS.escape&&(n=n.replace(/#([^\s"#']+)/g,(e,t)=>`#${CSS.escape(t)}`)),n),Bu=n=>n==null?`${n}`:Object.prototype.toString.call(n).match(/\s([a-z]+)/i)[1].toLowerCase(),Vu=n=>{do n+=Math.floor(Math.random()*Fu);while(document.getElementById(n));return n},zu=n=>{if(!n)return 0;let{transitionDuration:e,transitionDelay:t}=window.getComputedStyle(n),i=Number.parseFloat(e),r=Number.parseFloat(t);return!i&&!r?0:(e=e.split(",")[0],t=t.split(",")[0],(Number.parseFloat(e)+Number.parseFloat(t))*$u)},da=n=>{n.dispatchEvent(new Event(xo))},dt=n=>!n||typeof n!="object"?!1:(typeof n.jquery!="undefined"&&(n=n[0]),typeof n.nodeType!="undefined"),xt=n=>dt(n)?n.jquery?n[0]:n:typeof n=="string"&&n.length>0?document.querySelector(ua(n)):null,Cn=n=>{if(!dt(n)||n.getClientRects().length===0)return!1;let e=getComputedStyle(n).getPropertyValue("visibility")==="visible",t=n.closest("details:not([open])");if(!t)return e;if(t!==n){let i=n.closest("summary");if(i&&i.parentNode!==t||i===null)return!1}return e},Tt=n=>!n||n.nodeType!==Node.ELEMENT_NODE||n.classList.contains("disabled")?!0:typeof n.disabled!="undefined"?n.disabled:n.hasAttribute("disabled")&&n.getAttribute("disabled")!=="false",fa=n=>{if(!document.documentElement.attachShadow)return null;if(typeof n.getRootNode=="function"){let e=n.getRootNode();return e instanceof ShadowRoot?e:null}return n instanceof ShadowRoot?n:n.parentNode?fa(n.parentNode):null},Yi=()=>{},ei=n=>{n.offsetHeight},ha=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,ao=[],ju=n=>{document.readyState==="loading"?(ao.length||document.addEventListener("DOMContentLoaded",()=>{for(let e of ao)e()}),ao.push(n)):n()},Xe=()=>document.documentElement.dir==="rtl",Je=n=>{ju(()=>{let e=ha();if(e){let t=n.NAME,i=e.fn[t];e.fn[t]=n.jQueryInterface,e.fn[t].Constructor=n,e.fn[t].noConflict=()=>(e.fn[t]=i,n.jQueryInterface)}})},Pe=(n,e=[],t=n)=>typeof n=="function"?n.call(...e):t,pa=(n,e,t=!0)=>{if(!t){Pe(n);return}let r=zu(e)+5,o=!1,s=({target:a})=>{a===e&&(o=!0,e.removeEventListener(xo,s),Pe(n))};e.addEventListener(xo,s),setTimeout(()=>{o||da(e)},r)},Ao=(n,e,t,i)=>{let r=n.length,o=n.indexOf(e);return o===-1?!t&&i?n[r-1]:n[0]:(o+=t?1:-1,i&&(o=(o+r)%r),n[Math.max(0,Math.min(o,r-1))])},Wu=/[^.]*(?=\..*)\.|.*/,qu=/\..*/,Uu=/::\d+$/,lo={},Rs=1,ma={mouseenter:"mouseover",mouseleave:"mouseout"},Yu=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function ga(n,e){return e&&`${e}::${Rs++}`||n.uidEvent||Rs++}function va(n){let e=ga(n);return n.uidEvent=e,lo[e]=lo[e]||{},lo[e]}function Gu(n,e){return function t(i){return Do(i,{delegateTarget:n}),t.oneOff&&x.off(n,i.type,e),e.apply(n,[i])}}function Ku(n,e,t){return function i(r){let o=n.querySelectorAll(e);for(let{target:s}=r;s&&s!==this;s=s.parentNode)for(let a of o)if(a===s)return Do(r,{delegateTarget:s}),i.oneOff&&x.off(n,r.type,e,t),t.apply(s,[r])}}function ya(n,e,t=null){return Object.values(n).find(i=>i.callable===e&&i.delegationSelector===t)}function Ea(n,e,t){let i=typeof e=="string",r=i?t:e||t,o=ba(n);return Yu.has(o)||(o=n),[i,r,o]}function Ps(n,e,t,i,r){if(typeof e!="string"||!n)return;let[o,s,a]=Ea(e,t,i);e in ma&&(s=(m=>function(g){if(!g.relatedTarget||g.relatedTarget!==g.delegateTarget&&!g.delegateTarget.contains(g.relatedTarget))return m.call(this,g)})(s));let l=va(n),c=l[a]||(l[a]={}),u=ya(c,s,o?t:null);if(u){u.oneOff=u.oneOff&&r;return}let d=ga(s,e.replace(Wu,"")),p=o?Ku(n,t,s):Gu(n,s);p.delegationSelector=o?t:null,p.callable=s,p.oneOff=r,p.uidEvent=d,c[d]=p,n.addEventListener(a,p,o)}function To(n,e,t,i,r){let o=ya(e[t],i,r);o&&(n.removeEventListener(t,o,!!r),delete e[t][o.uidEvent])}function Xu(n,e,t,i){let r=e[t]||{};for(let[o,s]of Object.entries(r))o.includes(i)&&To(n,e,t,s.callable,s.delegationSelector)}function ba(n){return n=n.replace(qu,""),ma[n]||n}var x={on(n,e,t,i){Ps(n,e,t,i,!1)},one(n,e,t,i){Ps(n,e,t,i,!0)},off(n,e,t,i){if(typeof e!="string"||!n)return;let[r,o,s]=Ea(e,t,i),a=s!==e,l=va(n),c=l[s]||{},u=e.startsWith(".");if(typeof o!="undefined"){if(!Object.keys(c).length)return;To(n,l,s,o,r?t:null);return}if(u)for(let d of Object.keys(l))Xu(n,l,d,e.slice(1));for(let[d,p]of Object.entries(c)){let y=d.replace(Uu,"");(!a||e.includes(y))&&To(n,l,s,p.callable,p.delegationSelector)}},trigger(n,e,t){if(typeof e!="string"||!n)return null;let i=ha(),r=ba(e),o=e!==r,s=null,a=!0,l=!0,c=!1;o&&i&&(s=i.Event(e,t),i(n).trigger(s),a=!s.isPropagationStopped(),l=!s.isImmediatePropagationStopped(),c=s.isDefaultPrevented());let u=Do(new Event(e,{bubbles:a,cancelable:!0}),t);return c&&u.preventDefault(),l&&n.dispatchEvent(u),u.defaultPrevented&&s&&s.preventDefault(),u}};function Do(n,e={}){for(let[t,i]of Object.entries(e))try{n[t]=i}catch(r){Object.defineProperty(n,t,{configurable:!0,get(){return i}})}return n}function Fs(n){if(n==="true")return!0;if(n==="false")return!1;if(n===Number(n).toString())return Number(n);if(n===""||n==="null")return null;if(typeof n!="string")return n;try{return JSON.parse(decodeURIComponent(n))}catch(e){return n}}function co(n){return n.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}var ft={setDataAttribute(n,e,t){n.setAttribute(`data-bs-${co(e)}`,t)},removeDataAttribute(n,e){n.removeAttribute(`data-bs-${co(e)}`)},getDataAttributes(n){if(!n)return{};let e={},t=Object.keys(n.dataset).filter(i=>i.startsWith("bs")&&!i.startsWith("bsConfig"));for(let i of t){let r=i.replace(/^bs/,"");r=r.charAt(0).toLowerCase()+r.slice(1),e[r]=Fs(n.dataset[i])}return e},getDataAttribute(n,e){return Fs(n.getAttribute(`data-bs-${co(e)}`))}},Xt=class{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){let i=dt(t)?ft.getDataAttribute(t,"config"):{};return D(D(D(D({},this.constructor.Default),typeof i=="object"?i:{}),dt(t)?ft.getDataAttributes(t):{}),typeof e=="object"?e:{})}_typeCheckConfig(e,t=this.constructor.DefaultType){for(let[i,r]of Object.entries(t)){let o=e[i],s=dt(o)?"element":Bu(o);if(!new RegExp(r).test(s))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${i}" provided type "${s}" but expected type "${r}".`)}}},Qu="5.3.8",qe=class extends Xt{constructor(e,t){super(),e=xt(e),e&&(this._element=e,this._config=this._getConfig(t),so.set(this._element,this.constructor.DATA_KEY,this))}dispose(){so.remove(this._element,this.constructor.DATA_KEY),x.off(this._element,this.constructor.EVENT_KEY);for(let e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,i=!0){pa(e,t,i)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return so.get(xt(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,typeof t=="object"?t:null)}static get VERSION(){return Qu}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(e){return`${e}${this.EVENT_KEY}`}},uo=n=>{let e=n.getAttribute("data-bs-target");if(!e||e==="#"){let t=n.getAttribute("href");if(!t||!t.includes("#")&&!t.startsWith("."))return null;t.includes("#")&&!t.startsWith("#")&&(t=`#${t.split("#")[1]}`),e=t&&t!=="#"?t.trim():null}return e?e.split(",").map(t=>ua(t)).join(","):null},z={find(n,e=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(e,n))},findOne(n,e=document.documentElement){return Element.prototype.querySelector.call(e,n)},children(n,e){return[].concat(...n.children).filter(t=>t.matches(e))},parents(n,e){let t=[],i=n.parentNode.closest(e);for(;i;)t.push(i),i=i.parentNode.closest(e);return t},prev(n,e){let t=n.previousElementSibling;for(;t;){if(t.matches(e))return[t];t=t.previousElementSibling}return[]},next(n,e){let t=n.nextElementSibling;for(;t;){if(t.matches(e))return[t];t=t.nextElementSibling}return[]},focusableChildren(n){let e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(t=>`${t}:not([tabindex^="-"])`).join(",");return this.find(e,n).filter(t=>!Tt(t)&&Cn(t))},getSelectorFromElement(n){let e=uo(n);return e&&z.findOne(e)?e:null},getElementFromSelector(n){let e=uo(n);return e?z.findOne(e):null},getMultipleElementsFromSelector(n){let e=uo(n);return e?z.find(e):[]}},tr=(n,e="hide")=>{let t=`click.dismiss${n.EVENT_KEY}`,i=n.NAME;x.on(document,t,`[data-bs-dismiss="${i}"]`,function(r){if(["A","AREA"].includes(this.tagName)&&r.preventDefault(),Tt(this))return;let o=z.getElementFromSelector(this)||this.closest(`.${i}`);n.getOrCreateInstance(o)[e]()})},Ju="alert",Zu="bs.alert",_a=`.${Zu}`,ed=`close${_a}`,td=`closed${_a}`,nd="fade",id="show",Gi=class n extends qe{static get NAME(){return Ju}close(){if(x.trigger(this._element,ed).defaultPrevented)return;this._element.classList.remove(id);let t=this._element.classList.contains(nd);this._queueCallback(()=>this._destroyElement(),this._element,t)}_destroyElement(){this._element.remove(),x.trigger(this._element,td),this.dispose()}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};tr(Gi,"close");Je(Gi);var rd="button",od="bs.button",sd=`.${od}`,ad=".data-api",ld="active",$s='[data-bs-toggle="button"]',cd=`click${sd}${ad}`,Ki=class n extends qe{static get NAME(){return rd}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(ld))}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);e==="toggle"&&t[e]()})}};x.on(document,cd,$s,n=>{n.preventDefault();let e=n.target.closest($s);Ki.getOrCreateInstance(e).toggle()});Je(Ki);var ud="swipe",Sn=".bs.swipe",dd=`touchstart${Sn}`,fd=`touchmove${Sn}`,hd=`touchend${Sn}`,pd=`pointerdown${Sn}`,md=`pointerup${Sn}`,gd="touch",vd="pen",yd="pointer-event",Ed=40,bd={endCallback:null,leftCallback:null,rightCallback:null},_d={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"},Xi=class n extends Xt{constructor(e,t){super(),this._element=e,!(!e||!n.isSupported())&&(this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=!!window.PointerEvent,this._initEvents())}static get Default(){return bd}static get DefaultType(){return _d}static get NAME(){return ud}dispose(){x.off(this._element,Sn)}_start(e){if(!this._supportPointerEvents){this._deltaX=e.touches[0].clientX;return}this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX)}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),Pe(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){let e=Math.abs(this._deltaX);if(e<=Ed)return;let t=e/this._deltaX;this._deltaX=0,t&&Pe(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(x.on(this._element,pd,e=>this._start(e)),x.on(this._element,md,e=>this._end(e)),this._element.classList.add(yd)):(x.on(this._element,dd,e=>this._start(e)),x.on(this._element,fd,e=>this._move(e)),x.on(this._element,hd,e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&(e.pointerType===vd||e.pointerType===gd)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}},wd="carousel",xd="bs.carousel",Dt=`.${xd}`,wa=".data-api",Td="ArrowLeft",Cd="ArrowRight",Sd=500,Xn="next",En="prev",_n="left",qi="right",Ad=`slide${Dt}`,fo=`slid${Dt}`,Dd=`keydown${Dt}`,Od=`mouseenter${Dt}`,Ld=`mouseleave${Dt}`,Md=`dragstart${Dt}`,Nd=`load${Dt}${wa}`,kd=`click${Dt}${wa}`,xa="carousel",$i="active",Hd="slide",Id="carousel-item-end",Rd="carousel-item-start",Pd="carousel-item-next",Fd="carousel-item-prev",Ta=".active",Ca=".carousel-item",$d=Ta+Ca,Bd=".carousel-item img",Vd=".carousel-indicators",zd="[data-bs-slide], [data-bs-slide-to]",jd='[data-bs-ride="carousel"]',Wd={[Td]:qi,[Cd]:_n},qd={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Ud={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"},Jn=class n extends qe{constructor(e,t){super(e,t),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(Vd,this._element),this._addEventListeners(),this._config.ride===xa&&this.cycle()}static get Default(){return qd}static get DefaultType(){return Ud}static get NAME(){return wd}next(){this._slide(Xn)}nextWhenVisible(){!document.hidden&&Cn(this._element)&&this.next()}prev(){this._slide(En)}pause(){this._isSliding&&da(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){if(this._config.ride){if(this._isSliding){x.one(this._element,fo,()=>this.cycle());return}this.cycle()}}to(e){let t=this._getItems();if(e>t.length-1||e<0)return;if(this._isSliding){x.one(this._element,fo,()=>this.to(e));return}let i=this._getItemIndex(this._getActive());if(i===e)return;let r=e>i?Xn:En;this._slide(r,t[e])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&x.on(this._element,Dd,e=>this._keydown(e)),this._config.pause==="hover"&&(x.on(this._element,Od,()=>this.pause()),x.on(this._element,Ld,()=>this._maybeEnableCycle())),this._config.touch&&Xi.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(let i of z.find(Bd,this._element))x.on(i,Md,r=>r.preventDefault());let t={leftCallback:()=>this._slide(this._directionToOrder(_n)),rightCallback:()=>this._slide(this._directionToOrder(qi)),endCallback:()=>{this._config.pause==="hover"&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),Sd+this._config.interval))}};this._swipeHelper=new Xi(this._element,t)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;let t=Wd[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;let t=z.findOne(Ta,this._indicatorsElement);t.classList.remove($i),t.removeAttribute("aria-current");let i=z.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);i&&(i.classList.add($i),i.setAttribute("aria-current","true"))}_updateInterval(){let e=this._activeElement||this._getActive();if(!e)return;let t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(e,t=null){if(this._isSliding)return;let i=this._getActive(),r=e===Xn,o=t||Ao(this._getItems(),i,r,this._config.wrap);if(o===i)return;let s=this._getItemIndex(o),a=y=>x.trigger(this._element,y,{relatedTarget:o,direction:this._orderToDirection(e),from:this._getItemIndex(i),to:s});if(a(Ad).defaultPrevented||!i||!o)return;let c=!!this._interval;this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(s),this._activeElement=o;let u=r?Rd:Id,d=r?Pd:Fd;o.classList.add(d),ei(o),i.classList.add(u),o.classList.add(u);let p=()=>{o.classList.remove(u,d),o.classList.add($i),i.classList.remove($i,d,u),this._isSliding=!1,a(fo)};this._queueCallback(p,i,this._isAnimated()),c&&this.cycle()}_isAnimated(){return this._element.classList.contains(Hd)}_getActive(){return z.findOne($d,this._element)}_getItems(){return z.find(Ca,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return Xe()?e===_n?En:Xn:e===_n?Xn:En}_orderToDirection(e){return Xe()?e===En?_n:qi:e===En?qi:_n}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="number"){t.to(e);return}if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};x.on(document,kd,zd,function(n){let e=z.getElementFromSelector(this);if(!e||!e.classList.contains(xa))return;n.preventDefault();let t=Jn.getOrCreateInstance(e),i=this.getAttribute("data-bs-slide-to");if(i){t.to(i),t._maybeEnableCycle();return}if(ft.getDataAttribute(this,"slide")==="next"){t.next(),t._maybeEnableCycle();return}t.prev(),t._maybeEnableCycle()});x.on(window,Nd,()=>{let n=z.find(jd);for(let e of n)Jn.getOrCreateInstance(e)});Je(Jn);var Yd="collapse",Gd="bs.collapse",ti=`.${Gd}`,Kd=".data-api",Xd=`show${ti}`,Qd=`shown${ti}`,Jd=`hide${ti}`,Zd=`hidden${ti}`,ef=`click${ti}${Kd}`,ho="show",xn="collapse",Bi="collapsing",tf="collapsed",nf=`:scope .${xn} .${xn}`,rf="collapse-horizontal",of="width",sf="height",af=".collapse.show, .collapse.collapsing",Co='[data-bs-toggle="collapse"]',lf={parent:null,toggle:!0},cf={parent:"(null|element)",toggle:"boolean"},Ct=class n extends qe{constructor(e,t){super(e,t),this._isTransitioning=!1,this._triggerArray=[];let i=z.find(Co);for(let r of i){let o=z.getSelectorFromElement(r),s=z.find(o).filter(a=>a===this._element);o!==null&&s.length&&this._triggerArray.push(r)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return lf}static get DefaultType(){return cf}static get NAME(){return Yd}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let e=[];if(this._config.parent&&(e=this._getFirstLevelChildren(af).filter(a=>a!==this._element).map(a=>n.getOrCreateInstance(a,{toggle:!1}))),e.length&&e[0]._isTransitioning||x.trigger(this._element,Xd).defaultPrevented)return;for(let a of e)a.hide();let i=this._getDimension();this._element.classList.remove(xn),this._element.classList.add(Bi),this._element.style[i]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;let r=()=>{this._isTransitioning=!1,this._element.classList.remove(Bi),this._element.classList.add(xn,ho),this._element.style[i]="",x.trigger(this._element,Qd)},s=`scroll${i[0].toUpperCase()+i.slice(1)}`;this._queueCallback(r,this._element,!0),this._element.style[i]=`${this._element[s]}px`}hide(){if(this._isTransitioning||!this._isShown()||x.trigger(this._element,Jd).defaultPrevented)return;let t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,ei(this._element),this._element.classList.add(Bi),this._element.classList.remove(xn,ho);for(let r of this._triggerArray){let o=z.getElementFromSelector(r);o&&!this._isShown(o)&&this._addAriaAndCollapsedClass([r],!1)}this._isTransitioning=!0;let i=()=>{this._isTransitioning=!1,this._element.classList.remove(Bi),this._element.classList.add(xn),x.trigger(this._element,Zd)};this._element.style[t]="",this._queueCallback(i,this._element,!0)}_isShown(e=this._element){return e.classList.contains(ho)}_configAfterMerge(e){return e.toggle=!!e.toggle,e.parent=xt(e.parent),e}_getDimension(){return this._element.classList.contains(rf)?of:sf}_initializeChildren(){if(!this._config.parent)return;let e=this._getFirstLevelChildren(Co);for(let t of e){let i=z.getElementFromSelector(t);i&&this._addAriaAndCollapsedClass([t],this._isShown(i))}}_getFirstLevelChildren(e){let t=z.find(nf,this._config.parent);return z.find(e,this._config.parent).filter(i=>!t.includes(i))}_addAriaAndCollapsedClass(e,t){if(e.length)for(let i of e)i.classList.toggle(tf,!t),i.setAttribute("aria-expanded",t)}static jQueryInterface(e){let t={};return typeof e=="string"&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){let i=n.getOrCreateInstance(this,t);if(typeof e=="string"){if(typeof i[e]=="undefined")throw new TypeError(`No method named "${e}"`);i[e]()}})}};x.on(document,ef,Co,function(n){(n.target.tagName==="A"||n.delegateTarget&&n.delegateTarget.tagName==="A")&&n.preventDefault();for(let e of z.getMultipleElementsFromSelector(this))Ct.getOrCreateInstance(e,{toggle:!1}).toggle()});Je(Ct);var Bs="dropdown",uf="bs.dropdown",Jt=`.${uf}`,Oo=".data-api",df="Escape",Vs="Tab",ff="ArrowUp",zs="ArrowDown",hf=2,pf=`hide${Jt}`,mf=`hidden${Jt}`,gf=`show${Jt}`,vf=`shown${Jt}`,Sa=`click${Jt}${Oo}`,Aa=`keydown${Jt}${Oo}`,yf=`keyup${Jt}${Oo}`,wn="show",Ef="dropup",bf="dropend",_f="dropstart",wf="dropup-center",xf="dropdown-center",Gt='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Tf=`${Gt}.${wn}`,Ui=".dropdown-menu",Cf=".navbar",Sf=".navbar-nav",Af=".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",Df=Xe()?"top-end":"top-start",Of=Xe()?"top-start":"top-end",Lf=Xe()?"bottom-end":"bottom-start",Mf=Xe()?"bottom-start":"bottom-end",Nf=Xe()?"left-start":"right-start",kf=Xe()?"right-start":"left-start",Hf="top",If="bottom",Rf={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Pf={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"},St=class n extends qe{constructor(e,t){super(e,t),this._popper=null,this._parent=this._element.parentNode,this._menu=z.next(this._element,Ui)[0]||z.prev(this._element,Ui)[0]||z.findOne(Ui,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Rf}static get DefaultType(){return Pf}static get NAME(){return Bs}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Tt(this._element)||this._isShown())return;let e={relatedTarget:this._element};if(!x.trigger(this._element,gf,e).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(Sf))for(let i of[].concat(...document.body.children))x.on(i,"mouseover",Yi);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(wn),this._element.classList.add(wn),x.trigger(this._element,vf,e)}}hide(){if(Tt(this._element)||!this._isShown())return;let e={relatedTarget:this._element};this._completeHide(e)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(e){if(!x.trigger(this._element,pf,e).defaultPrevented){if("ontouchstart"in document.documentElement)for(let i of[].concat(...document.body.children))x.off(i,"mouseover",Yi);this._popper&&this._popper.destroy(),this._menu.classList.remove(wn),this._element.classList.remove(wn),this._element.setAttribute("aria-expanded","false"),ft.removeDataAttribute(this._menu,"popper"),x.trigger(this._element,mf,e)}}_getConfig(e){if(e=super._getConfig(e),typeof e.reference=="object"&&!dt(e.reference)&&typeof e.reference.getBoundingClientRect!="function")throw new TypeError(`${Bs.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return e}_createPopper(){if(typeof Fi=="undefined")throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org/docs/v2/)");let e=this._element;this._config.reference==="parent"?e=this._parent:dt(this._config.reference)?e=xt(this._config.reference):typeof this._config.reference=="object"&&(e=this._config.reference);let t=this._getPopperConfig();this._popper=Kn(e,this._menu,t)}_isShown(){return this._menu.classList.contains(wn)}_getPlacement(){let e=this._parent;if(e.classList.contains(bf))return Nf;if(e.classList.contains(_f))return kf;if(e.classList.contains(wf))return Hf;if(e.classList.contains(xf))return If;let t=getComputedStyle(this._menu).getPropertyValue("--bs-position").trim()==="end";return e.classList.contains(Ef)?t?Of:Df:t?Mf:Lf}_detectNavbar(){return this._element.closest(Cf)!==null}_getOffset(){let{offset:e}=this._config;return typeof e=="string"?e.split(",").map(t=>Number.parseInt(t,10)):typeof e=="function"?t=>e(t,this._element):e}_getPopperConfig(){let e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||this._config.display==="static")&&(ft.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),D(D({},e),Pe(this._config.popperConfig,[void 0,e]))}_selectMenuItem({key:e,target:t}){let i=z.find(Af,this._menu).filter(r=>Cn(r));i.length&&Ao(i,t,e===zs,!i.includes(t)).focus()}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}static clearMenus(e){if(e.button===hf||e.type==="keyup"&&e.key!==Vs)return;let t=z.find(Tf);for(let i of t){let r=n.getInstance(i);if(!r||r._config.autoClose===!1)continue;let o=e.composedPath(),s=o.includes(r._menu);if(o.includes(r._element)||r._config.autoClose==="inside"&&!s||r._config.autoClose==="outside"&&s||r._menu.contains(e.target)&&(e.type==="keyup"&&e.key===Vs||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;let a={relatedTarget:r._element};e.type==="click"&&(a.clickEvent=e),r._completeHide(a)}}static dataApiKeydownHandler(e){let t=/input|textarea/i.test(e.target.tagName),i=e.key===df,r=[ff,zs].includes(e.key);if(!r&&!i||t&&!i)return;e.preventDefault();let o=this.matches(Gt)?this:z.prev(this,Gt)[0]||z.next(this,Gt)[0]||z.findOne(Gt,e.delegateTarget.parentNode),s=n.getOrCreateInstance(o);if(r){e.stopPropagation(),s.show(),s._selectMenuItem(e);return}s._isShown()&&(e.stopPropagation(),s.hide(),o.focus())}};x.on(document,Aa,Gt,St.dataApiKeydownHandler);x.on(document,Aa,Ui,St.dataApiKeydownHandler);x.on(document,Sa,St.clearMenus);x.on(document,yf,St.clearMenus);x.on(document,Sa,Gt,function(n){n.preventDefault(),St.getOrCreateInstance(this).toggle()});Je(St);var Da="backdrop",Ff="fade",js="show",Ws=`mousedown.bs.${Da}`,$f={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Bf={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"},Qi=class extends Xt{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return $f}static get DefaultType(){return Bf}static get NAME(){return Da}show(e){if(!this._config.isVisible){Pe(e);return}this._append();let t=this._getElement();this._config.isAnimated&&ei(t),t.classList.add(js),this._emulateAnimation(()=>{Pe(e)})}hide(e){if(!this._config.isVisible){Pe(e);return}this._getElement().classList.remove(js),this._emulateAnimation(()=>{this.dispose(),Pe(e)})}dispose(){this._isAppended&&(x.off(this._element,Ws),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){let e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add(Ff),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=xt(e.rootElement),e}_append(){if(this._isAppended)return;let e=this._getElement();this._config.rootElement.append(e),x.on(e,Ws,()=>{Pe(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){pa(e,this._getElement(),this._config.isAnimated)}},Vf="focustrap",zf="bs.focustrap",Ji=`.${zf}`,jf=`focusin${Ji}`,Wf=`keydown.tab${Ji}`,qf="Tab",Uf="forward",qs="backward",Yf={autofocus:!0,trapElement:null},Gf={autofocus:"boolean",trapElement:"element"},Zi=class extends Xt{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Yf}static get DefaultType(){return Gf}static get NAME(){return Vf}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),x.off(document,Ji),x.on(document,jf,e=>this._handleFocusin(e)),x.on(document,Wf,e=>this._handleKeydown(e)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,x.off(document,Ji))}_handleFocusin(e){let{trapElement:t}=this._config;if(e.target===document||e.target===t||t.contains(e.target))return;let i=z.focusableChildren(t);i.length===0?t.focus():this._lastTabNavDirection===qs?i[i.length-1].focus():i[0].focus()}_handleKeydown(e){e.key===qf&&(this._lastTabNavDirection=e.shiftKey?qs:Uf)}},Us=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Ys=".sticky-top",Vi="padding-right",Gs="margin-right",Zn=class{constructor(){this._element=document.body}getWidth(){let e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){let e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,Vi,t=>t+e),this._setElementAttributes(Us,Vi,t=>t+e),this._setElementAttributes(Ys,Gs,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,Vi),this._resetElementAttributes(Us,Vi),this._resetElementAttributes(Ys,Gs)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,i){let r=this.getWidth(),o=s=>{if(s!==this._element&&window.innerWidth>s.clientWidth+r)return;this._saveInitialAttribute(s,t);let a=window.getComputedStyle(s).getPropertyValue(t);s.style.setProperty(t,`${i(Number.parseFloat(a))}px`)};this._applyManipulationCallback(e,o)}_saveInitialAttribute(e,t){let i=e.style.getPropertyValue(t);i&&ft.setDataAttribute(e,t,i)}_resetElementAttributes(e,t){let i=r=>{let o=ft.getDataAttribute(r,t);if(o===null){r.style.removeProperty(t);return}ft.removeDataAttribute(r,t),r.style.setProperty(t,o)};this._applyManipulationCallback(e,i)}_applyManipulationCallback(e,t){if(dt(e)){t(e);return}for(let i of z.find(e,this._element))t(i)}},Kf="modal",Xf="bs.modal",Qe=`.${Xf}`,Qf=".data-api",Jf="Escape",Zf=`hide${Qe}`,eh=`hidePrevented${Qe}`,Oa=`hidden${Qe}`,La=`show${Qe}`,th=`shown${Qe}`,nh=`resize${Qe}`,ih=`click.dismiss${Qe}`,rh=`mousedown.dismiss${Qe}`,oh=`keydown.dismiss${Qe}`,sh=`click${Qe}${Qf}`,Ks="modal-open",ah="fade",Xs="show",po="modal-static",lh=".modal.show",ch=".modal-dialog",uh=".modal-body",dh='[data-bs-toggle="modal"]',fh={backdrop:!0,focus:!0,keyboard:!0},hh={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"},tt=class n extends qe{constructor(e,t){super(e,t),this._dialog=z.findOne(ch,this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new Zn,this._addEventListeners()}static get Default(){return fh}static get DefaultType(){return hh}static get NAME(){return Kf}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||this._isTransitioning||x.trigger(this._element,La,{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Ks),this._adjustDialog(),this._backdrop.show(()=>this._showElement(e)))}hide(){!this._isShown||this._isTransitioning||x.trigger(this._element,Zf).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Xs),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated()))}dispose(){x.off(window,Qe),x.off(this._dialog,Qe),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Qi({isVisible:!!this._config.backdrop,isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Zi({trapElement:this._element})}_showElement(e){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;let t=z.findOne(uh,this._dialog);t&&(t.scrollTop=0),ei(this._element),this._element.classList.add(Xs);let i=()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,x.trigger(this._element,th,{relatedTarget:e})};this._queueCallback(i,this._dialog,this._isAnimated())}_addEventListeners(){x.on(this._element,oh,e=>{if(e.key===Jf){if(this._config.keyboard){this.hide();return}this._triggerBackdropTransition()}}),x.on(window,nh,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),x.on(this._element,rh,e=>{x.one(this._element,ih,t=>{if(!(this._element!==e.target||this._element!==t.target)){if(this._config.backdrop==="static"){this._triggerBackdropTransition();return}this._config.backdrop&&this.hide()}})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Ks),this._resetAdjustments(),this._scrollBar.reset(),x.trigger(this._element,Oa)})}_isAnimated(){return this._element.classList.contains(ah)}_triggerBackdropTransition(){if(x.trigger(this._element,eh).defaultPrevented)return;let t=this._element.scrollHeight>document.documentElement.clientHeight,i=this._element.style.overflowY;i==="hidden"||this._element.classList.contains(po)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(po),this._queueCallback(()=>{this._element.classList.remove(po),this._queueCallback(()=>{this._element.style.overflowY=i},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){let e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._scrollBar.getWidth(),i=t>0;if(i&&!e){let r=Xe()?"paddingLeft":"paddingRight";this._element.style[r]=`${t}px`}if(!i&&e){let r=Xe()?"paddingRight":"paddingLeft";this._element.style[r]=`${t}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){let i=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof i[e]=="undefined")throw new TypeError(`No method named "${e}"`);i[e](t)}})}};x.on(document,sh,dh,function(n){let e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&n.preventDefault(),x.one(e,La,r=>{r.defaultPrevented||x.one(e,Oa,()=>{Cn(this)&&this.focus()})});let t=z.findOne(lh);t&&tt.getInstance(t).hide(),tt.getOrCreateInstance(e).toggle(this)});tr(tt);Je(tt);var ph="offcanvas",mh="bs.offcanvas",mt=`.${mh}`,Ma=".data-api",gh=`load${mt}${Ma}`,vh="Escape",Qs="show",Js="showing",Zs="hiding",yh="offcanvas-backdrop",Na=".offcanvas.show",Eh=`show${mt}`,bh=`shown${mt}`,_h=`hide${mt}`,ea=`hidePrevented${mt}`,ka=`hidden${mt}`,wh=`resize${mt}`,xh=`click${mt}${Ma}`,Th=`keydown.dismiss${mt}`,Ch='[data-bs-toggle="offcanvas"]',Sh={backdrop:!0,keyboard:!0,scroll:!1},Ah={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"},At=class n extends qe{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Sh}static get DefaultType(){return Ah}static get NAME(){return ph}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){if(this._isShown||x.trigger(this._element,Eh,{relatedTarget:e}).defaultPrevented)return;this._isShown=!0,this._backdrop.show(),this._config.scroll||new Zn().hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Js);let i=()=>{(!this._config.scroll||this._config.backdrop)&&this._focustrap.activate(),this._element.classList.add(Qs),this._element.classList.remove(Js),x.trigger(this._element,bh,{relatedTarget:e})};this._queueCallback(i,this._element,!0)}hide(){if(!this._isShown||x.trigger(this._element,_h).defaultPrevented)return;this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Zs),this._backdrop.hide();let t=()=>{this._element.classList.remove(Qs,Zs),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||new Zn().reset(),x.trigger(this._element,ka)};this._queueCallback(t,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){let e=()=>{if(this._config.backdrop==="static"){x.trigger(this._element,ea);return}this.hide()},t=!!this._config.backdrop;return new Qi({className:yh,isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?e:null})}_initializeFocusTrap(){return new Zi({trapElement:this._element})}_addEventListeners(){x.on(this._element,Th,e=>{if(e.key===vh){if(this._config.keyboard){this.hide();return}x.trigger(this._element,ea)}})}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};x.on(document,xh,Ch,function(n){let e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),Tt(this))return;x.one(e,ka,()=>{Cn(this)&&this.focus()});let t=z.findOne(Na);t&&t!==e&&At.getInstance(t).hide(),At.getOrCreateInstance(e).toggle(this)});x.on(window,gh,()=>{for(let n of z.find(Na))At.getOrCreateInstance(n).show()});x.on(window,wh,()=>{for(let n of z.find("[aria-modal][class*=show][class*=offcanvas-]"))getComputedStyle(n).position!=="fixed"&&At.getOrCreateInstance(n).hide()});tr(At);Je(At);var Dh=/^aria-[\w-]*$/i,Ha={"*":["class","dir","id","lang","role",Dh],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Oh=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Lh=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Mh=(n,e)=>{let t=n.nodeName.toLowerCase();return e.includes(t)?Oh.has(t)?!!Lh.test(n.nodeValue):!0:e.filter(i=>i instanceof RegExp).some(i=>i.test(t))};function Nh(n,e,t){if(!n.length)return n;if(t&&typeof t=="function")return t(n);let r=new window.DOMParser().parseFromString(n,"text/html"),o=[].concat(...r.body.querySelectorAll("*"));for(let s of o){let a=s.nodeName.toLowerCase();if(!Object.keys(e).includes(a)){s.remove();continue}let l=[].concat(...s.attributes),c=[].concat(e["*"]||[],e[a]||[]);for(let u of l)Mh(u,c)||s.removeAttribute(u.nodeName)}return r.body.innerHTML}var kh="TemplateFactory",Hh={allowList:Ha,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Ih={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Rh={entry:"(string|element|function|null)",selector:"(string|element)"},So=class extends Xt{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return Hh}static get DefaultType(){return Ih}static get NAME(){return kh}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content=D(D({},this._config.content),e),this}toHtml(){let e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(let[r,o]of Object.entries(this._config.content))this._setContent(e,o,r);let t=e.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&t.classList.add(...i.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(let[t,i]of Object.entries(e))super._typeCheckConfig({selector:t,entry:i},Rh)}_setContent(e,t,i){let r=z.findOne(i,e);if(r){if(t=this._resolvePossibleFunction(t),!t){r.remove();return}if(dt(t)){this._putElementInTemplate(xt(t),r);return}if(this._config.html){r.innerHTML=this._maybeSanitize(t);return}r.textContent=t}}_maybeSanitize(e){return this._config.sanitize?Nh(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return Pe(e,[void 0,this])}_putElementInTemplate(e,t){if(this._config.html){t.innerHTML="",t.append(e);return}t.textContent=e.textContent}},Ph="tooltip",Fh=new Set(["sanitize","allowList","sanitizeFn"]),mo="fade",$h="modal",zi="show",Bh=".tooltip-inner",ta=`.${$h}`,na="hide.bs.modal",Qn="hover",go="focus",vo="click",Vh="manual",zh="hide",jh="hidden",Wh="show",qh="shown",Uh="inserted",Yh="click",Gh="focusin",Kh="focusout",Xh="mouseenter",Qh="mouseleave",Jh={AUTO:"auto",TOP:"top",RIGHT:Xe()?"left":"right",BOTTOM:"bottom",LEFT:Xe()?"right":"left"},Zh={allowList:Ha,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ep={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"},ht=class n extends qe{constructor(e,t){if(typeof Fi=="undefined")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org/docs/v2/)");super(e,t),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return Zh}static get DefaultType(){return ep}static get NAME(){return Ph}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){if(this._isEnabled){if(this._isShown()){this._leave();return}this._enter()}}dispose(){clearTimeout(this._timeout),x.off(this._element.closest(ta),na,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!(this._isWithContent()&&this._isEnabled))return;let e=x.trigger(this._element,this.constructor.eventName(Wh)),i=(fa(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(e.defaultPrevented||!i)return;this._disposePopper();let r=this._getTipElement();this._element.setAttribute("aria-describedby",r.getAttribute("id"));let{container:o}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(o.append(r),x.trigger(this._element,this.constructor.eventName(Uh))),this._popper=this._createPopper(r),r.classList.add(zi),"ontouchstart"in document.documentElement)for(let a of[].concat(...document.body.children))x.on(a,"mouseover",Yi);let s=()=>{x.trigger(this._element,this.constructor.eventName(qh)),this._isHovered===!1&&this._leave(),this._isHovered=!1};this._queueCallback(s,this.tip,this._isAnimated())}hide(){if(!this._isShown()||x.trigger(this._element,this.constructor.eventName(zh)).defaultPrevented)return;if(this._getTipElement().classList.remove(zi),"ontouchstart"in document.documentElement)for(let r of[].concat(...document.body.children))x.off(r,"mouseover",Yi);this._activeTrigger[vo]=!1,this._activeTrigger[go]=!1,this._activeTrigger[Qn]=!1,this._isHovered=null;let i=()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),x.trigger(this._element,this.constructor.eventName(jh)))};this._queueCallback(i,this.tip,this._isAnimated())}update(){this._popper&&this._popper.update()}_isWithContent(){return!!this._getTitle()}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){let t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(mo,zi),t.classList.add(`bs-${this.constructor.NAME}-auto`);let i=Vu(this.constructor.NAME).toString();return t.setAttribute("id",i),this._isAnimated()&&t.classList.add(mo),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new So(se(D({},this._config),{content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)})),this._templateFactory}_getContentForTemplate(){return{[Bh]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(mo)}_isShown(){return this.tip&&this.tip.classList.contains(zi)}_createPopper(e){let t=Pe(this._config.placement,[this,e,this._element]),i=Jh[t.toUpperCase()];return Kn(this._element,e,this._getPopperConfig(i))}_getOffset(){let{offset:e}=this._config;return typeof e=="string"?e.split(",").map(t=>Number.parseInt(t,10)):typeof e=="function"?t=>e(t,this._element):e}_resolvePossibleFunction(e){return Pe(e,[this._element,this._element])}_getPopperConfig(e){let t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:i=>{this._getTipElement().setAttribute("data-popper-placement",i.state.placement)}}]};return D(D({},t),Pe(this._config.popperConfig,[void 0,t]))}_setListeners(){let e=this._config.trigger.split(" ");for(let t of e)if(t==="click")x.on(this._element,this.constructor.eventName(Yh),this._config.selector,i=>{let r=this._initializeOnDelegatedTarget(i);r._activeTrigger[vo]=!(r._isShown()&&r._activeTrigger[vo]),r.toggle()});else if(t!==Vh){let i=t===Qn?this.constructor.eventName(Xh):this.constructor.eventName(Gh),r=t===Qn?this.constructor.eventName(Qh):this.constructor.eventName(Kh);x.on(this._element,i,this._config.selector,o=>{let s=this._initializeOnDelegatedTarget(o);s._activeTrigger[o.type==="focusin"?go:Qn]=!0,s._enter()}),x.on(this._element,r,this._config.selector,o=>{let s=this._initializeOnDelegatedTarget(o);s._activeTrigger[o.type==="focusout"?go:Qn]=s._element.contains(o.relatedTarget),s._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},x.on(this._element.closest(ta),na,this._hideModalHandler)}_fixTitle(){let e=this._element.getAttribute("title");e&&(!this._element.getAttribute("aria-label")&&!this._element.textContent.trim()&&this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title"))}_enter(){if(this._isShown()||this._isHovered){this._isHovered=!0;return}this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show)}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){let t=ft.getDataAttributes(this._element);for(let i of Object.keys(t))Fh.has(i)&&delete t[i];return e=D(D({},t),typeof e=="object"&&e?e:{}),e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=e.container===!1?document.body:xt(e.container),typeof e.delay=="number"&&(e.delay={show:e.delay,hide:e.delay}),typeof e.title=="number"&&(e.title=e.title.toString()),typeof e.content=="number"&&(e.content=e.content.toString()),e}_getDelegateConfig(){let e={};for(let[t,i]of Object.entries(this._config))this.constructor.Default[t]!==i&&(e[t]=i);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}};Je(ht);var tp="popover",np=".popover-header",ip=".popover-body",rp=se(D({},ht.Default),{content:"",offset:[0,8],placement:"right",template:'',trigger:"click"}),op=se(D({},ht.DefaultType),{content:"(null|string|element|function)"}),Tn=class n extends ht{static get Default(){return rp}static get DefaultType(){return op}static get NAME(){return tp}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[np]:this._getTitle(),[ip]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e]()}})}};Je(Tn);var sp="scrollspy",ap="bs.scrollspy",Lo=`.${ap}`,lp=".data-api",cp=`activate${Lo}`,ia=`click${Lo}`,up=`load${Lo}${lp}`,dp="dropdown-item",bn="active",fp='[data-bs-spy="scroll"]',yo="[href]",hp=".nav, .list-group",ra=".nav-link",pp=".nav-item",mp=".list-group-item",gp=`${ra}, ${pp} > ${ra}, ${mp}`,vp=".dropdown",yp=".dropdown-toggle",Ep={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},bp={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"},er=class n extends qe{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement=getComputedStyle(this._element).overflowY==="visible"?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Ep}static get DefaultType(){return bp}static get NAME(){return sp}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(let e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=xt(e.target)||document.body,e.rootMargin=e.offset?`${e.offset}px 0px -30%`:e.rootMargin,typeof e.threshold=="string"&&(e.threshold=e.threshold.split(",").map(t=>Number.parseFloat(t))),e}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(x.off(this._config.target,ia),x.on(this._config.target,ia,yo,e=>{let t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();let i=this._rootElement||window,r=t.offsetTop-this._element.offsetTop;if(i.scrollTo){i.scrollTo({top:r,behavior:"smooth"});return}i.scrollTop=r}}))}_getNewObserver(){let e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(t=>this._observerCallback(t),e)}_observerCallback(e){let t=s=>this._targetLinks.get(`#${s.target.id}`),i=s=>{this._previousScrollData.visibleEntryTop=s.target.offsetTop,this._process(t(s))},r=(this._rootElement||document.documentElement).scrollTop,o=r>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=r;for(let s of e){if(!s.isIntersecting){this._activeTarget=null,this._clearActiveClass(t(s));continue}let a=s.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&a){if(i(s),!r)return;continue}!o&&!a&&i(s)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;let e=z.find(yo,this._config.target);for(let t of e){if(!t.hash||Tt(t))continue;let i=z.findOne(decodeURI(t.hash),this._element);Cn(i)&&(this._targetLinks.set(decodeURI(t.hash),t),this._observableSections.set(t.hash,i))}}_process(e){this._activeTarget!==e&&(this._clearActiveClass(this._config.target),this._activeTarget=e,e.classList.add(bn),this._activateParents(e),x.trigger(this._element,cp,{relatedTarget:e}))}_activateParents(e){if(e.classList.contains(dp)){z.findOne(yp,e.closest(vp)).classList.add(bn);return}for(let t of z.parents(e,hp))for(let i of z.prev(t,gp))i.classList.add(bn)}_clearActiveClass(e){e.classList.remove(bn);let t=z.find(`${yo}.${bn}`,e);for(let i of t)i.classList.remove(bn)}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};x.on(window,up,()=>{for(let n of z.find(fp))er.getOrCreateInstance(n)});Je(er);var _p="tab",wp="bs.tab",Zt=`.${wp}`,xp=`hide${Zt}`,Tp=`hidden${Zt}`,Cp=`show${Zt}`,Sp=`shown${Zt}`,Ap=`click${Zt}`,Dp=`keydown${Zt}`,Op=`load${Zt}`,Lp="ArrowLeft",oa="ArrowRight",Mp="ArrowUp",sa="ArrowDown",Eo="Home",aa="End",Kt="active",la="fade",bo="show",Np="dropdown",Ia=".dropdown-toggle",kp=".dropdown-menu",_o=`:not(${Ia})`,Hp='.list-group, .nav, [role="tablist"]',Ip=".nav-item, .list-group-item",Rp=`.nav-link${_o}, .list-group-item${_o}, [role="tab"]${_o}`,Ra='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',wo=`${Rp}, ${Ra}`,Pp=`.${Kt}[data-bs-toggle="tab"], .${Kt}[data-bs-toggle="pill"], .${Kt}[data-bs-toggle="list"]`,Qt=class n extends qe{constructor(e){super(e),this._parent=this._element.closest(Hp),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),x.on(this._element,Dp,t=>this._keydown(t)))}static get NAME(){return _p}show(){let e=this._element;if(this._elemIsActive(e))return;let t=this._getActiveElem(),i=t?x.trigger(t,xp,{relatedTarget:e}):null;x.trigger(e,Cp,{relatedTarget:t}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(t,e),this._activate(e,t))}_activate(e,t){if(!e)return;e.classList.add(Kt),this._activate(z.getElementFromSelector(e));let i=()=>{if(e.getAttribute("role")!=="tab"){e.classList.add(bo);return}e.removeAttribute("tabindex"),e.setAttribute("aria-selected",!0),this._toggleDropDown(e,!0),x.trigger(e,Sp,{relatedTarget:t})};this._queueCallback(i,e,e.classList.contains(la))}_deactivate(e,t){if(!e)return;e.classList.remove(Kt),e.blur(),this._deactivate(z.getElementFromSelector(e));let i=()=>{if(e.getAttribute("role")!=="tab"){e.classList.remove(bo);return}e.setAttribute("aria-selected",!1),e.setAttribute("tabindex","-1"),this._toggleDropDown(e,!1),x.trigger(e,Tp,{relatedTarget:t})};this._queueCallback(i,e,e.classList.contains(la))}_keydown(e){if(![Lp,oa,Mp,sa,Eo,aa].includes(e.key))return;e.stopPropagation(),e.preventDefault();let t=this._getChildren().filter(r=>!Tt(r)),i;if([Eo,aa].includes(e.key))i=t[e.key===Eo?0:t.length-1];else{let r=[oa,sa].includes(e.key);i=Ao(t,e.target,r,!0)}i&&(i.focus({preventScroll:!0}),n.getOrCreateInstance(i).show())}_getChildren(){return z.find(wo,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(let i of t)this._setInitialAttributesOnChild(i)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);let t=this._elemIsActive(e),i=this._getOuterElement(e);e.setAttribute("aria-selected",t),i!==e&&this._setAttributeIfNotExists(i,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){let t=z.getElementFromSelector(e);t&&(this._setAttributeIfNotExists(t,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(t,"aria-labelledby",`${e.id}`))}_toggleDropDown(e,t){let i=this._getOuterElement(e);if(!i.classList.contains(Np))return;let r=(o,s)=>{let a=z.findOne(o,i);a&&a.classList.toggle(s,t)};r(Ia,Kt),r(kp,bo),i.setAttribute("aria-expanded",t)}_setAttributeIfNotExists(e,t,i){e.hasAttribute(t)||e.setAttribute(t,i)}_elemIsActive(e){return e.classList.contains(Kt)}_getInnerElement(e){return e.matches(wo)?e:z.findOne(wo,e)}_getOuterElement(e){return e.closest(Ip)||e}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this);if(typeof e=="string"){if(t[e]===void 0||e.startsWith("_")||e==="constructor")throw new TypeError(`No method named "${e}"`);t[e]()}})}};x.on(document,Ap,Ra,function(n){["A","AREA"].includes(this.tagName)&&n.preventDefault(),!Tt(this)&&Qt.getOrCreateInstance(this).show()});x.on(window,Op,()=>{for(let n of z.find(Pp))Qt.getOrCreateInstance(n)});Je(Qt);var Fp="toast",$p="bs.toast",Ot=`.${$p}`,Bp=`mouseover${Ot}`,Vp=`mouseout${Ot}`,zp=`focusin${Ot}`,jp=`focusout${Ot}`,Wp=`hide${Ot}`,qp=`hidden${Ot}`,Up=`show${Ot}`,Yp=`shown${Ot}`,Gp="fade",ca="hide",ji="show",Wi="showing",Kp={animation:"boolean",autohide:"boolean",delay:"number"},Xp={animation:!0,autohide:!0,delay:5e3},pt=class n extends qe{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return Xp}static get DefaultType(){return Kp}static get NAME(){return Fp}show(){if(x.trigger(this._element,Up).defaultPrevented)return;this._clearTimeout(),this._config.animation&&this._element.classList.add(Gp);let t=()=>{this._element.classList.remove(Wi),x.trigger(this._element,Yp),this._maybeScheduleHide()};this._element.classList.remove(ca),ei(this._element),this._element.classList.add(ji,Wi),this._queueCallback(t,this._element,this._config.animation)}hide(){if(!this.isShown()||x.trigger(this._element,Wp).defaultPrevented)return;let t=()=>{this._element.classList.add(ca),this._element.classList.remove(Wi,ji),x.trigger(this._element,qp)};this._element.classList.add(Wi),this._queueCallback(t,this._element,this._config.animation)}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(ji),super.dispose()}isShown(){return this._element.classList.contains(ji)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":{this._hasMouseInteraction=t;break}case"focusin":case"focusout":{this._hasKeyboardInteraction=t;break}}if(t){this._clearTimeout();return}let i=e.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){x.on(this._element,Bp,e=>this._onInteraction(e,!0)),x.on(this._element,Vp,e=>this._onInteraction(e,!1)),x.on(this._element,zp,e=>this._onInteraction(e,!0)),x.on(this._element,jp,e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){let t=n.getOrCreateInstance(this,e);if(typeof e=="string"){if(typeof t[e]=="undefined")throw new TypeError(`No method named "${e}"`);t[e](this)}})}};tr(pt);Je(pt);var Qp=(function(){"use strict";let htmx={onLoad:null,process:null,on:null,off:null,trigger:null,ajax:null,find:null,findAll:null,closest:null,values:function(n,e){return getInputValues(n,e||"post").values},remove:null,addClass:null,removeClass:null,toggleClass:null,takeClass:null,swap:null,defineExtension:null,removeExtension:null,logAll:null,logNone:null,logger:null,config:{historyEnabled:!0,historyCacheSize:10,refreshOnHistoryMiss:!1,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:!0,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:!0,allowScriptTags:!0,inlineScriptNonce:"",inlineStyleNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:!1,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",scrollBehavior:"instant",defaultFocusScroll:!1,getCacheBusterParam:!1,globalViewTransitions:!1,methodsThatUseUrlParams:["get","delete"],selfRequestsOnly:!0,ignoreTitle:!1,scrollIntoViewOnBoost:!0,triggerSpecsCache:null,disableInheritance:!1,responseHandling:[{code:"204",swap:!1},{code:"[23]..",swap:!0},{code:"[45]..",swap:!1,error:!0}],allowNestedOobSwaps:!0,historyRestoreAsHxRequest:!0,reportValidityOfForms:!1},parseInterval:null,location,_:null,version:"2.0.10"};htmx.onLoad=onLoadHelper,htmx.process=processNode,htmx.on=addEventListenerImpl,htmx.off=removeEventListenerImpl,htmx.trigger=triggerEvent,htmx.ajax=ajaxHelper,htmx.find=find,htmx.findAll=findAll,htmx.closest=closest,htmx.remove=removeElement,htmx.addClass=addClassToElement,htmx.removeClass=removeClassFromElement,htmx.toggleClass=toggleClassOnElement,htmx.takeClass=takeClassForElement,htmx.swap=swap,htmx.defineExtension=defineExtension,htmx.removeExtension=removeExtension,htmx.logAll=logAll,htmx.logNone=logNone,htmx.parseInterval=parseInterval,htmx._=internalEval;let internalAPI={addTriggerHandler,bodyContains,canAccessLocalStorage,findThisElement,filterValues,swap,hasAttribute,getAttributeValue,getClosestAttributeValue,getClosestMatch,getExpressionVars,getHeaders,getInputValues,getInternalData,getSwapSpecification,getTriggerSpecs,getTarget,makeFragment,mergeObjects,makeSettleInfo,oobSwap,querySelectorExt,settleImmediately,shouldCancel,triggerEvent,triggerErrorEvent,withExtensions},VERBS=["get","post","put","delete","patch"],VERB_SELECTOR=VERBS.map(function(n){return"[hx-"+n+"], [data-hx-"+n+"]"}).join(", ");function parseInterval(n){if(n==null)return;let e=NaN;return n.slice(-2)=="ms"?e=parseFloat(n.slice(0,-2)):n.slice(-1)=="s"?e=parseFloat(n.slice(0,-1))*1e3:n.slice(-1)=="m"?e=parseFloat(n.slice(0,-1))*1e3*60:e=parseFloat(n),isNaN(e)?void 0:e}function getRawAttribute(n,e){return n instanceof Element&&n.getAttribute(e)}function hasAttribute(n,e){return!!n.hasAttribute&&(n.hasAttribute(e)||n.hasAttribute("data-"+e))}function getAttributeValue(n,e){return getRawAttribute(n,e)||getRawAttribute(n,"data-"+e)}function parentElt(n){let e=n.parentElement;return!e&&n.parentNode instanceof ShadowRoot?n.parentNode:e}function getDocument(){return document}function getRootNode(n,e){return n.getRootNode?n.getRootNode({composed:e}):getDocument()}function getClosestMatch(n,e){for(;n&&!e(n);)n=parentElt(n);return n||null}function getAttributeValueWithDisinheritance(n,e,t){let i=getAttributeValue(e,t),r=getAttributeValue(e,"hx-disinherit");var o=getAttributeValue(e,"hx-inherit");if(n!==e){if(htmx.config.disableInheritance)return o&&(o==="*"||o.split(" ").indexOf(t)>=0)?i:null;if(r&&(r==="*"||r.split(" ").indexOf(t)>=0))return"unset"}return i}function getClosestAttributeValue(n,e){let t=null;if(getClosestMatch(n,function(i){return!!(t=getAttributeValueWithDisinheritance(n,asElement(i),e))}),t!=="unset")return t}function matches(n,e){return n instanceof Element&&n.matches(e)}function getStartTag(n){let t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i.exec(n);return t?t[1].toLowerCase():""}function parseHTML(n){return"parseHTMLUnsafe"in Document?Document.parseHTMLUnsafe(n):new DOMParser().parseFromString(n,"text/html")}function takeChildrenFor(n,e){for(;e.childNodes.length>0;)n.append(e.childNodes[0])}function duplicateScript(n){let e=getDocument().createElement("script");return forEach(n.attributes,function(t){e.setAttribute(t.name,t.value)}),e.textContent=n.textContent,e.async=!1,htmx.config.inlineScriptNonce&&(e.nonce=htmx.config.inlineScriptNonce),e}function isJavaScriptScriptNode(n){return n.matches("script")&&(n.type==="text/javascript"||n.type==="module"||n.type==="")}function normalizeScriptTags(n){Array.from(n.querySelectorAll("script")).forEach(e=>{if(isJavaScriptScriptNode(e)){let t=duplicateScript(e),i=e.parentNode;try{i.insertBefore(t,e)}catch(r){logError(r)}finally{e.remove()}}})}function makeFragment(n){let e=n.replace(/]*)?>[\s\S]*?<\/head>/i,""),t=getStartTag(e),i;if(t==="html"){i=new DocumentFragment;let o=parseHTML(n);takeChildrenFor(i,o.body),i.title=o.title}else if(t==="body"){i=new DocumentFragment;let o=parseHTML(e);takeChildrenFor(i,o.body),i.title=o.title}else{let o=parseHTML('");i=o.querySelector("template").content,i.title=o.title;var r=i.querySelector("title");r&&r.parentNode===i&&(r.remove(),i.title=r.innerText)}return i&&(htmx.config.allowScriptTags?normalizeScriptTags(i):i.querySelectorAll("script").forEach(o=>o.remove())),i}function maybeCall(n){n&&n()}function isType(n,e){return Object.prototype.toString.call(n)==="[object "+e+"]"}function isFunction(n){return typeof n=="function"}function isRawObject(n){return isType(n,"Object")}function getInternalData(n){let e="htmx-internal-data",t=n[e];return t||(t=n[e]={}),t}function toArray(n){let e=[];if(n)for(let t=0;t=0}function bodyContains(n){return n.getRootNode({composed:!0})===document}function splitOnWhitespace(n){return n.trim().split(/\s+/)}function mergeObjects(n,e){for(let t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);return n}function parseJSON(n){try{return JSON.parse(n)}catch(e){return logError(e),null}}function canAccessLocalStorage(){let n="htmx:sessionStorageTest";try{return sessionStorage.setItem(n,n),sessionStorage.removeItem(n),!0}catch(e){return!1}}function normalizePath(n){try{let e=new URL(n,window.location.href);n=e.pathname+e.search}catch(e){}return n!="/"&&(n=n.replace(/\/+$/,"")),n}function internalEval(str){return maybeEval(getDocument().body,function(){return eval(str)})}function onLoadHelper(n){return htmx.on("htmx:load",function(t){n(t.detail.elt)})}function logAll(){htmx.logger=function(n,e,t){console&&console.log(e,n,t)}}function logNone(){htmx.logger=null}function find(n,e){return typeof n!="string"?n.querySelector(e):find(getDocument(),n)}function findAll(n,e){return typeof n!="string"?n.querySelectorAll(e):findAll(getDocument(),n)}function getWindow(){return window}function removeElement(n,e){n=resolveTarget(n),e?getWindow().setTimeout(function(){removeElement(n),n=null},e):parentElt(n).removeChild(n)}function asElement(n){return n instanceof Element?n:null}function asHtmlElement(n){return n instanceof HTMLElement?n:null}function asString(n){return typeof n=="string"?n:null}function asParentNode(n){return n instanceof Element||n instanceof Document||n instanceof DocumentFragment?n:null}function addClassToElement(n,e,t){n=asElement(resolveTarget(n)),n&&(t?getWindow().setTimeout(function(){addClassToElement(n,e),n=null},t):n.classList&&n.classList.add(e))}function removeClassFromElement(n,e,t){let i=asElement(resolveTarget(n));i&&(t?getWindow().setTimeout(function(){removeClassFromElement(i,e),i=null},t):i.classList&&(i.classList.remove(e),i.classList.length===0&&i.removeAttribute("class")))}function toggleClassOnElement(n,e){n=resolveTarget(n),n.classList.toggle(e)}function takeClassForElement(n,e){n=resolveTarget(n),forEach(n.parentElement.children,function(t){removeClassFromElement(t,e)}),addClassToElement(asElement(n),e)}function closest(n,e){return n=asElement(resolveTarget(n)),n?n.closest(e):null}function startsWith(n,e){return n.substring(0,e.length)===e}function endsWith(n,e){return n.substring(n.length-e.length)===e}function normalizeSelector(n){let e=n.trim();return startsWith(e,"<")&&endsWith(e,"/>")?e.substring(1,e.length-2):e}function querySelectorAllExt(n,e,t){if(e.indexOf("global ")===0)return querySelectorAllExt(n,e.slice(7),!0);n=resolveTarget(n);let i=[];{let s=0,a=0;for(let l=0;l"&&s--}a0;){let s=normalizeSelector(i.shift()),a;s.indexOf("closest ")===0?a=closest(asElement(n),normalizeSelector(s.slice(8))):s.indexOf("find ")===0?a=find(asParentNode(n),normalizeSelector(s.slice(5))):s==="next"||s==="nextElementSibling"?a=asElement(n).nextElementSibling:s.indexOf("next ")===0?a=scanForwardQuery(n,normalizeSelector(s.slice(5)),!!t):s==="previous"||s==="previousElementSibling"?a=asElement(n).previousElementSibling:s.indexOf("previous ")===0?a=scanBackwardsQuery(n,normalizeSelector(s.slice(9)),!!t):s==="document"?a=document:s==="window"?a=window:s==="body"?a=document.body:s==="root"?a=getRootNode(n,!!t):s==="host"?a=n.getRootNode().host:o.push(s),a&&r.push(a)}if(o.length>0){let s=o.join(","),a=asParentNode(getRootNode(n,!!t));r.push(...toArray(a.querySelectorAll(s)))}return r}var scanForwardQuery=function(n,e,t){let i=asParentNode(getRootNode(n,t)).querySelectorAll(e);for(let r=0;r=0;r--){let o=i[r];if(o.compareDocumentPosition(n)===Node.DOCUMENT_POSITION_FOLLOWING)return o}};function querySelectorExt(n,e){return typeof n!="string"?querySelectorAllExt(n,e)[0]:querySelectorAllExt(getDocument().body,n)[0]}function resolveTarget(n,e){return typeof n=="string"?find(asParentNode(e)||document,n):n}function processEventArgs(n,e,t,i){return isFunction(e)?{target:getDocument().body,event:asString(n),listener:e,options:t}:{target:resolveTarget(n),event:asString(e),listener:t,options:i}}function addEventListenerImpl(n,e,t,i){return ready(function(){let o=processEventArgs(n,e,t,i);o.target.addEventListener(o.event,o.listener,o.options)}),isFunction(e)?e:t}function removeEventListenerImpl(n,e,t){return ready(function(){let i=processEventArgs(n,e,t);i.target.removeEventListener(i.event,i.listener)}),isFunction(e)?e:t}let DUMMY_ELT=getDocument().createElement("output");function findAttributeTargets(n,e){let t=getClosestAttributeValue(n,e);if(t){if(t==="this")return[findThisElement(n,e)];{let i=querySelectorAllExt(n,t);if(/(^|,)(\s*)inherit(\s*)($|,)/.test(t)){let o=asElement(getClosestMatch(n,function(s){return s!==n&&hasAttribute(asElement(s),e)}));o&&i.push(...findAttributeTargets(o,e))}return i.length===0?(logError('The selector "'+t+'" on '+e+" returned no matches!"),[DUMMY_ELT]):i}}}function findThisElement(n,e){return asElement(getClosestMatch(n,function(t){return getAttributeValue(asElement(t),e)!=null}))}function getTarget(n){let e=getClosestAttributeValue(n,"hx-target");return e?e==="this"?findThisElement(n,"hx-target"):querySelectorExt(n,e):getInternalData(n).boosted?getDocument().body:n}function shouldSettleAttribute(n){return htmx.config.attributesToSettle.includes(n)}function cloneAttributes(n,e){forEach(Array.from(n.attributes),function(t){!e.hasAttribute(t.name)&&shouldSettleAttribute(t.name)&&n.removeAttribute(t.name)}),forEach(e.attributes,function(t){shouldSettleAttribute(t.name)&&n.setAttribute(t.name,t.value)})}function isInlineSwap(n,e){let t=getExtensions(e);for(let i=0;i0?(o=n.substring(0,n.indexOf(":")),r=n.substring(n.indexOf(":")+1)):o=n),e.removeAttribute("hx-swap-oob"),e.removeAttribute("data-hx-swap-oob");let s=querySelectorAllExt(i,r,!1);return s.length?(forEach(s,function(a){let l,c=e.cloneNode(!0);l=getDocument().createDocumentFragment(),l.appendChild(c),isInlineSwap(o,a)||(l=asParentNode(c));let u={shouldSwap:!0,target:a,fragment:l};triggerEvent(a,"htmx:oobBeforeSwap",u)&&(a=u.target,u.shouldSwap&&(handlePreservedElements(l),swapWithStyle(o,a,a,l,t),restorePreservedElements()),forEach(t.elts,function(d){triggerEvent(d,"htmx:oobAfterSwap",u)}))}),e.parentNode.removeChild(e)):(e.parentNode.removeChild(e),triggerErrorEvent(getDocument().body,"htmx:oobErrorNoTarget",{content:e,target:r})),n}function restorePreservedElements(){let n=find("#--htmx-preserve-pantry--");if(n){for(let e of[...n.children]){let t=find("#"+e.id);t.parentNode.moveBefore(e,t),t.remove()}n.remove()}}function handlePreservedElements(n){forEach(findAll(n,"[hx-preserve], [data-hx-preserve]"),function(e){let t=getAttributeValue(e,"id"),i=getDocument().getElementById(t);if(i!=null)if(e.moveBefore){let r=find("#--htmx-preserve-pantry--");r==null&&(getDocument().body.insertAdjacentHTML("afterend","
"),r=find("#--htmx-preserve-pantry--")),r.moveBefore(i,null)}else e.parentNode.replaceChild(i,e)})}function handleAttributes(n,e,t){forEach(e.querySelectorAll("[id]"),function(i){let r=getRawAttribute(i,"id");if(r&&r.length>0){let o=asParentNode(n),s=o&&o.querySelector(CSS.escape(i.tagName)+"#"+CSS.escape(r));if(s&&s!==o){let a=i.cloneNode();cloneAttributes(i,s),t.tasks.push(function(){cloneAttributes(i,a)})}}})}function makeAjaxLoadTask(n){return function(){removeClassFromElement(n,htmx.config.addedClass),processNode(asElement(n)),processFocus(asParentNode(n)),triggerEvent(n,"htmx:load")}}function processFocus(n){let e="[autofocus]",t=asHtmlElement(matches(n,e)?n:n.querySelector(e));t!=null&&t.focus()}function insertNodesBefore(n,e,t,i){for(handleAttributes(n,t,i);t.childNodes.length>0;){let r=t.firstChild;addClassToElement(asElement(r),htmx.config.addedClass),n.insertBefore(r,e),r.nodeType!==Node.TEXT_NODE&&r.nodeType!==Node.COMMENT_NODE&&i.tasks.push(makeAjaxLoadTask(r))}}function stringHash(n,e){let t=0;for(;t0}function swap(n,e,t,i){i||(i={});let r=null,o=null,s=function(){maybeCall(i.beforeSwapCallback),n=resolveTarget(n);let c=i.contextElement?getRootNode(i.contextElement,!1):getDocument(),u=document.activeElement,d={};d={elt:u,start:u?u.selectionStart:null,end:u?u.selectionEnd:null};let p=makeSettleInfo(n);if(t.swapStyle==="textContent")n.textContent=e;else{let m=makeFragment(e);if(p.title=i.title||m.title,i.historyRequest&&(m=m.querySelector("[hx-history-elt],[data-hx-history-elt]")||m),i.selectOOB){let g=i.selectOOB.split(",");for(let _=0;_0?getWindow().setTimeout(y,t.settleDelay):y()},a=htmx.config.globalViewTransitions;t.hasOwnProperty("transition")&&(a=t.transition);let l=i.contextElement||getDocument();if(a&&triggerEvent(l,"htmx:beforeTransition",i.eventInfo)&&typeof Promise!="undefined"&&document.startViewTransition){let c=new Promise(function(d,p){r=d,o=p}),u=s;s=function(){document.startViewTransition(function(){return u(),c})}}try{t!=null&&t.swapDelay&&t.swapDelay>0?getWindow().setTimeout(s,t.swapDelay):s()}catch(c){throw triggerErrorEvent(l,"htmx:swapError",i.eventInfo),maybeCall(o),c}}function handleTriggerHeader(n,e,t){let i=n.getResponseHeader(e);if(i.indexOf("{")===0){let r=parseJSON(i);for(let o in r)if(r.hasOwnProperty(o)){let s=r[o];isRawObject(s)?t=s.target!==void 0?s.target:t:s={value:s},triggerEvent(t,o,s)}}else{let r=i.split(",");for(let o=0;o0;){let s=e[0];if(s==="]"){if(i--,i===0){o===null&&(r=r+"true"),e.shift(),r+=")})";try{let a=maybeEval(n,function(){return Function(r)()},function(){return!0});return a.source=r,a}catch(a){return triggerErrorEvent(getDocument().body,"htmx:syntax:error",{error:a,source:r}),null}}}else s==="["&&i++;isPossibleRelativeReference(s,o,t)?r+="(("+t+"."+s+") ? ("+t+"."+s+") : (window."+s+"))":r=r+s,o=e.shift()}}}function consumeUntil(n,e){let t="";for(;n.length>0&&!e.test(n[0]);)t+=n.shift();return t}function consumeCSSSelector(n){let e;return n.length>0&&COMBINED_SELECTOR_START.test(n[0])?(n.shift(),e=consumeUntil(n,COMBINED_SELECTOR_END).trim(),n.shift()):e=consumeUntil(n,WHITESPACE_OR_COMMA),e}let INPUT_SELECTOR="input, textarea, select";function parseAndCacheTrigger(n,e,t){let i=[],r=tokenizeString(e);do{consumeUntil(r,NOT_WHITESPACE);let a=r.length,l=consumeUntil(r,/[,\[\s]/);if(l!=="")if(l==="every"){let c={trigger:"every"};consumeUntil(r,NOT_WHITESPACE),c.pollInterval=parseInterval(consumeUntil(r,/[,\[\s]/)),consumeUntil(r,NOT_WHITESPACE);var o=maybeGenerateConditional(n,r,"event");o&&(c.eventFilter=o),i.push(c)}else{let c={trigger:l};var o=maybeGenerateConditional(n,r,"event");for(o&&(c.eventFilter=o),consumeUntil(r,NOT_WHITESPACE);r.length>0&&r[0]!==",";){let d=r.shift();if(d==="changed")c.changed=!0;else if(d==="once")c.once=!0;else if(d==="consume")c.consume=!0;else if(d==="delay"&&r[0]===":")r.shift(),c.delay=parseInterval(consumeUntil(r,WHITESPACE_OR_COMMA));else if(d==="from"&&r[0]===":"){if(r.shift(),COMBINED_SELECTOR_START.test(r[0]))var s=consumeCSSSelector(r);else{var s=consumeUntil(r,WHITESPACE_OR_COMMA);if(s==="closest"||s==="find"||s==="next"||s==="previous"){r.shift();let y=consumeCSSSelector(r);y.length>0&&(s+=" "+y)}}c.from=s}else d==="target"&&r[0]===":"?(r.shift(),c.target=consumeCSSSelector(r)):d==="throttle"&&r[0]===":"?(r.shift(),c.throttle=parseInterval(consumeUntil(r,WHITESPACE_OR_COMMA))):d==="queue"&&r[0]===":"?(r.shift(),c.queue=consumeUntil(r,WHITESPACE_OR_COMMA)):d==="root"&&r[0]===":"?(r.shift(),c[d]=consumeCSSSelector(r)):d==="threshold"&&r[0]===":"?(r.shift(),c[d]=consumeUntil(r,WHITESPACE_OR_COMMA)):triggerErrorEvent(n,"htmx:syntax:error",{token:r.shift()});consumeUntil(r,NOT_WHITESPACE)}i.push(c)}r.length===a&&triggerErrorEvent(n,"htmx:syntax:error",{token:r.shift()}),consumeUntil(r,NOT_WHITESPACE)}while(r[0]===","&&r.shift());return t&&(t[e]=i),i}function getTriggerSpecs(n){let e=getAttributeValue(n,"hx-trigger"),t=[];if(e){let i=htmx.config.triggerSpecsCache;t=i&&i[e]||parseAndCacheTrigger(n,e,i)}return t.length>0?t:matches(n,"form")?[{trigger:"submit"}]:matches(n,'input[type="button"], input[type="submit"]')?[{trigger:"click"}]:matches(n,INPUT_SELECTOR)?[{trigger:"change"}]:[{trigger:"click"}]}function cancelPolling(n){getInternalData(n).cancelled=!0}function processPolling(n,e,t){let i=getInternalData(n);i.timeout=getWindow().setTimeout(function(){bodyContains(n)&&i.cancelled!==!0&&(maybeFilterEvent(t,n,makeEvent("hx:poll:trigger",{triggerSpec:t,target:n}))||e(n),processPolling(n,e,t))},t.pollInterval)}function isLocalLink(n){return location.hostname===n.hostname&&getRawAttribute(n,"href")&&getRawAttribute(n,"href").indexOf("#")!==0}function eltIsDisabled(n){return closest(n,htmx.config.disableSelector)}function boostElement(n,e,t){if(n instanceof HTMLAnchorElement&&isLocalLink(n)&&(n.target===""||n.target==="_self")||n.tagName==="FORM"&&String(getRawAttribute(n,"method")).toLowerCase()!=="dialog"){e.boosted=!0;let i,r;if(n.tagName==="A")i="get",r=getRawAttribute(n,"href");else{let o=getRawAttribute(n,"method");i=o?o.toLowerCase():"get",r=getRawAttribute(n,"action"),(r==null||r==="")&&(r=location.href),i==="get"&&r.includes("?")&&(r=r.replace(/\?[^#]+/,""))}t.forEach(function(o){addEventListener(n,function(s,a){let l=asElement(s);if(eltIsDisabled(l)){cleanUpElement(l);return}issueAjaxRequest(i,r,l,a)},e,o,!0)})}}function shouldCancel(n,e){if(n.type==="submit"&&e.tagName==="FORM")return!0;if(n.type==="click"){let t=e.closest('input[type="submit"], button');if(t&&t.form&&t.type==="submit")return!0;let i=e.closest("a"),r=/^#.+/;if(i&&i.href&&!r.test(i.getAttribute("href")))return!0}return!1}function ignoreBoostedAnchorCtrlClick(n,e){return getInternalData(n).boosted&&n instanceof HTMLAnchorElement&&e.type==="click"&&(e.ctrlKey||e.metaKey)}function maybeFilterEvent(n,e,t){let i=n.eventFilter;if(i)try{return i.call(e,t)!==!0}catch(r){let o=i.source;return triggerErrorEvent(getDocument().body,"htmx:eventFilter:error",{error:r,source:o}),!0}return!1}function addEventListener(n,e,t,i,r){let o=getInternalData(n),s;i.from?s=querySelectorAllExt(n,i.from):s=[n],i.changed&&("lastValue"in o||(o.lastValue=new WeakMap),s.forEach(function(a){o.lastValue.has(i)||o.lastValue.set(i,new WeakMap),o.lastValue.get(i).set(a,a.value)})),forEach(s,function(a){let l=function(c){if(!bodyContains(n)){a.removeEventListener(i.trigger,l);return}if(ignoreBoostedAnchorCtrlClick(n,c)||((r||shouldCancel(c,a))&&c.preventDefault(),maybeFilterEvent(i,n,c)))return;let u=getInternalData(c);if(u.triggerSpec=i,u.handledFor==null&&(u.handledFor=[]),u.handledFor.indexOf(n)<0){if(u.handledFor.push(n),i.consume&&c.stopPropagation(),i.target&&c.target&&!matches(asElement(c.target),i.target))return;if(i.once){if(o.triggeredOnce)return;o.triggeredOnce=!0}if(i.changed){let d=c.target,p=d.value,y=o.lastValue.get(i);if(y.has(d)&&y.get(d)===p)return;y.set(d,p)}if(o.delayed&&clearTimeout(o.delayed),o.throttle)return;i.throttle>0?o.throttle||(triggerEvent(n,"htmx:trigger"),e(n,c),o.throttle=getWindow().setTimeout(function(){o.throttle=null},i.throttle)):i.delay>0?o.delayed=getWindow().setTimeout(function(){triggerEvent(n,"htmx:trigger"),e(n,c)},i.delay):(triggerEvent(n,"htmx:trigger"),e(n,c))}};t.listenerInfos==null&&(t.listenerInfos=[]),t.listenerInfos.push({trigger:i.trigger,listener:l,on:a}),a.addEventListener(i.trigger,l)})}let windowIsScrolling=!1,scrollHandler=null;function initScrollHandler(){scrollHandler||(scrollHandler=function(){windowIsScrolling=!0},window.addEventListener("scroll",scrollHandler),window.addEventListener("resize",scrollHandler),setInterval(function(){windowIsScrolling&&(windowIsScrolling=!1,forEach(getDocument().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(n){maybeReveal(n)}))},200))}function maybeReveal(n){!hasAttribute(n,"data-hx-revealed")&&isScrolledIntoView(n)&&(n.setAttribute("data-hx-revealed","true"),getInternalData(n).initHash?triggerEvent(n,"revealed"):n.addEventListener("htmx:afterProcessNode",function(){triggerEvent(n,"revealed")},{once:!0}))}function loadImmediately(n,e,t,i){let r=function(){t.loaded||(t.loaded=!0,triggerEvent(n,"htmx:trigger"),e(n))};i>0?getWindow().setTimeout(r,i):r()}function processVerbs(n,e,t){let i=!1;return forEach(VERBS,function(r){if(hasAttribute(n,"hx-"+r)){let o=getAttributeValue(n,"hx-"+r);i=!0,e.path=o,e.verb=r,t.forEach(function(s){addTriggerHandler(n,s,e,function(a,l){let c=asElement(a);if(eltIsDisabled(c)){cleanUpElement(c);return}issueAjaxRequest(r,o,c,l)})})}}),i}function addTriggerHandler(n,e,t,i){if(e.trigger==="revealed")initScrollHandler(),addEventListener(n,i,t,e),maybeReveal(asElement(n));else if(e.trigger==="intersect"){let r={};e.root&&(r.root=querySelectorExt(n,e.root)),e.threshold&&(r.threshold=parseFloat(e.threshold)),new IntersectionObserver(function(s){for(let a=0;a0?(t.polling=!0,processPolling(asElement(n),i,e)):addEventListener(n,i,t,e)}function shouldProcessHxOn(n){let e=asElement(n);if(!e)return!1;let t=e.attributes;for(let i=0;i", "+o).join(""))}else return[]}function maybeSetLastButtonClicked(n){let e=getTargetButton(n.target),t=getRelatedFormData(n);t&&(t.lastButtonClicked=e)}function maybeUnsetLastButtonClicked(n){let e=getRelatedFormData(n);e&&(e.lastButtonClicked=null)}function getTargetButton(n){return closest(asElement(n),"button, input[type='submit']")}function getRelatedForm(n){return n.form||closest(n,"form")}function getRelatedFormData(n){let e=getTargetButton(n.target);if(!e)return;let t=getRelatedForm(e);if(t)return getInternalData(t)}function initButtonTracking(n){n.addEventListener("click",maybeSetLastButtonClicked),n.addEventListener("focusin",maybeSetLastButtonClicked),n.addEventListener("focusout",maybeUnsetLastButtonClicked)}function addHxOnEventHandler(n,e,t){let i=getInternalData(n);Array.isArray(i.onHandlers)||(i.onHandlers=[]);let r,o=function(s){maybeEval(n,function(){eltIsDisabled(n)||(r||(r=new Function("event",t)),r.call(n,s))})};n.addEventListener(e,o),i.onHandlers.push({event:e,listener:o})}function processHxOnWildcard(n){deInitOnHandlers(n);for(let e=0;ehtmx.config.historyCacheSize;)o.shift();for(;o.length>0;)try{sessionStorage.setItem("htmx-history-cache",JSON.stringify(o));break}catch(a){triggerErrorEvent(getDocument().body,"htmx:historyCacheError",{cause:a,cache:o}),o.shift()}}function getCachedHistory(n){if(!canAccessLocalStorage())return null;n=normalizePath(n);let e=parseJSON(sessionStorage.getItem("htmx-history-cache"))||[];for(let t=0;t=200&&this.status<400?(i.response=this.response,triggerEvent(getDocument().body,"htmx:historyCacheMissLoad",i),swap(i.historyElt,i.response,t,{contextElement:i.historyElt,historyRequest:!0}),setCurrentPathForHistory(i.path),triggerEvent(getDocument().body,"htmx:historyRestore",{path:n,cacheMiss:!0,serverResponse:i.response})):triggerErrorEvent(getDocument().body,"htmx:historyCacheMissLoadError",i)},triggerEvent(getDocument().body,"htmx:historyCacheMiss",i)&&e.send()}function restoreHistory(n){saveCurrentPageToHistory(),n=n||location.pathname+location.search;let e=getCachedHistory(n);if(e){let t={swapStyle:"innerHTML",swapDelay:0,settleDelay:0,scroll:e.scroll},i={path:n,item:e,historyElt:getHistoryElement(),swapSpec:t};triggerEvent(getDocument().body,"htmx:historyCacheHit",i)&&(swap(i.historyElt,e.content,t,{contextElement:i.historyElt,title:e.title}),setCurrentPathForHistory(i.path),triggerEvent(getDocument().body,"htmx:historyRestore",i))}else htmx.config.refreshOnHistoryMiss?htmx.location.reload(!0):loadHistoryFromServer(n)}function addRequestIndicatorClasses(n){let e=findAttributeTargets(n,"hx-indicator");return e==null&&(e=[n]),forEach(e,function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||0)+1,addClassToElement(t,htmx.config.requestClass)}),e}function disableElements(n){let e=findAttributeTargets(n,"hx-disabled-elt");return e==null&&(e=[]),forEach(e,function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||0)+1,t.hasAttribute("disabled")||(t.setAttribute("disabled",""),t.setAttribute("data-disabled-by-htmx",""))}),e}function removeRequestIndicators(n,e){forEach(n.concat(e),function(t){let i=getInternalData(t);i.requestCount=(i.requestCount||1)-1}),forEach(n,function(t){getInternalData(t).requestCount===0&&removeClassFromElement(t,htmx.config.requestClass)}),forEach(e,function(t){getInternalData(t).requestCount===0&&t.hasAttribute("data-disabled-by-htmx")&&(t.removeAttribute("disabled"),t.removeAttribute("data-disabled-by-htmx"))})}function haveSeenNode(n,e){for(let t=0;te.indexOf(r)<0):i=i.filter(r=>r!==e),t.delete(n),forEach(i,r=>t.append(n,r))}}function getValueFromInput(n){return n instanceof HTMLSelectElement&&n.multiple?toArray(n.querySelectorAll("option:checked")).map(function(e){return e.value}):n instanceof HTMLInputElement&&n.files?toArray(n.files):n.value}function processInputValue(n,e,t,i,r){if(!(i==null||haveSeenNode(n,i))){if(n.push(i),shouldInclude(i)){let o=getRawAttribute(i,"name");addValueToFormData(o,getValueFromInput(i),e),r&&validateElement(i,t)}i instanceof HTMLFormElement&&(forEach(i.elements,function(o){n.indexOf(o)>=0?removeValueFromFormData(o.name,getValueFromInput(o),e):n.push(o),r&&validateElement(o,t)}),new FormData(i).forEach(function(o,s){o instanceof File&&o.name===""||addValueToFormData(s,o,e)}))}}function validateElement(n,e){let t=n;t.willValidate&&(triggerEvent(t,"htmx:validation:validate"),t.checkValidity()||(triggerEvent(t,"htmx:validation:failed",{message:t.validationMessage,validity:t.validity})&&!e.length&&htmx.config.reportValidityOfForms&&t.reportValidity(),e.push({elt:t,message:t.validationMessage,validity:t.validity})))}function overrideFormData(n,e){for(let t of e.keys())n.delete(t);return e.forEach(function(t,i){n.append(i,t)}),n}function getInputValues(n,e){let t=[],i=new FormData,r=new FormData,o=[],s=getInternalData(n);s.lastButtonClicked&&!bodyContains(s.lastButtonClicked)&&(s.lastButtonClicked=null);let a=n instanceof HTMLFormElement&&n.noValidate!==!0||getAttributeValue(n,"hx-validate")==="true";if(s.lastButtonClicked&&(a=a&&s.lastButtonClicked.formNoValidate!==!0),e!=="get"&&processInputValue(t,r,o,getRelatedForm(n),a),processInputValue(t,i,o,n,a),s.lastButtonClicked||n.tagName==="BUTTON"||n.tagName==="INPUT"&&getRawAttribute(n,"type")==="submit"){let c=s.lastButtonClicked||n,u=getRawAttribute(c,"name");addValueToFormData(u,c.value,r)}let l=findAttributeTargets(n,"hx-include");return forEach(l,function(c){processInputValue(t,i,o,asElement(c),a),matches(c,"form")||forEach(asParentNode(c).querySelectorAll(INPUT_SELECTOR),function(u){processInputValue(t,i,o,u,a)})}),overrideFormData(i,r),{errors:o,formData:i,values:formDataProxy(i)}}function appendParam(n,e,t){n!==""&&(n+="&"),String(t)==="[object Object]"&&(t=JSON.stringify(t));let i=encodeURIComponent(t);return n+=encodeURIComponent(e)+"="+i,n}function urlEncode(n){n=formDataFromObject(n);let e="";return n.forEach(function(t,i){e=appendParam(e,i,t)}),e}function getHeaders(n,e,t){let i={"HX-Request":"true","HX-Trigger":getRawAttribute(n,"id"),"HX-Trigger-Name":getRawAttribute(n,"name"),"HX-Target":getAttributeValue(e,"id"),"HX-Current-URL":location.href};return getValuesForElement(n,"hx-headers",!1,i),t!==void 0&&(i["HX-Prompt"]=t),getInternalData(n).boosted&&(i["HX-Boosted"]="true"),i}function filterValues(n,e){let t=getClosestAttributeValue(e,"hx-params");if(t){if(t==="none")return new FormData;if(t==="*")return n;if(t.indexOf("not ")===0)return forEach(t.slice(4).split(","),function(i){i=i.trim(),n.delete(i)}),n;{let i=new FormData;return forEach(t.split(","),function(r){r=r.trim(),n.has(r)&&n.getAll(r).forEach(function(o){i.append(r,o)})}),i}}else return n}function isAnchorLink(n){return!!getRawAttribute(n,"href")&&getRawAttribute(n,"href").indexOf("#")>=0}function getSwapSpecification(n,e){let t=e||getClosestAttributeValue(n,"hx-swap"),i={swapStyle:getInternalData(n).boosted?"innerHTML":htmx.config.defaultSwapStyle,swapDelay:htmx.config.defaultSwapDelay,settleDelay:htmx.config.defaultSettleDelay};if(htmx.config.scrollIntoViewOnBoost&&getInternalData(n).boosted&&!isAnchorLink(n)&&(i.show="top"),t){let s=splitOnWhitespace(t);if(s.length>0)for(let a=0;a0?r.join(":"):null;i.scroll=u,i.scrollTarget=o}else if(l.indexOf("show:")===0){var r=l.slice(5).split(":");let d=r.pop();var o=r.length>0?r.join(":"):null;i.show=d,i.showTarget=o}else if(l.indexOf("focus-scroll:")===0){let c=l.slice(13);i.focusScroll=c=="true"}else a==0?i.swapStyle=l:logError("Unknown modifier in hx-swap: "+l)}}return i}function usesFormData(n){return getClosestAttributeValue(n,"hx-encoding")==="multipart/form-data"||matches(n,"form")&&getRawAttribute(n,"enctype")==="multipart/form-data"}function encodeParamsForBody(n,e,t){let i=null;return withExtensions(e,function(r){i==null&&(i=r.encodeParameters(n,t,e))}),i!=null?i:usesFormData(e)?overrideFormData(new FormData,formDataFromObject(t)):urlEncode(t)}function makeSettleInfo(n){return{tasks:[],elts:[n]}}function updateScrollState(n,e){let t=n[0],i=n[n.length-1];if(e.scroll){var r=null;e.scrollTarget&&(r=asElement(querySelectorExt(t,e.scrollTarget))),e.scroll==="top"&&(t||r)&&(r=r||t,r.scrollTop=0),e.scroll==="bottom"&&(i||r)&&(r=r||i,r.scrollTop=r.scrollHeight),typeof e.scroll=="number"&&getWindow().setTimeout(function(){window.scrollTo(0,e.scroll)},0)}if(e.show){var r=null;if(e.showTarget){let s=e.showTarget;e.showTarget==="window"&&(s="body"),r=asElement(querySelectorExt(t,s))}e.show==="top"&&(t||r)&&(r=r||t,r.scrollIntoView({block:"start",behavior:htmx.config.scrollBehavior})),e.show==="bottom"&&(i||r)&&(r=r||i,r.scrollIntoView({block:"end",behavior:htmx.config.scrollBehavior}))}}function getValuesForElement(n,e,t,i,r){if(i==null&&(i={}),n==null)return i;let o=getAttributeValue(n,e);if(o){let s=o.trim(),a=t;if(s==="unset")return null;s.indexOf("javascript:")===0?(s=s.slice(11),a=!0):s.indexOf("js:")===0&&(s=s.slice(3),a=!0),s.indexOf("{")!==0&&(s="{"+s+"}");let l;a?l=maybeEval(n,function(){return r?Function("event","return ("+s+")").call(n,r):Function("return ("+s+")").call(n)},{}):l=parseJSON(s);for(let c in l)l.hasOwnProperty(c)&&i[c]==null&&(i[c]=l[c])}return getValuesForElement(asElement(parentElt(n)),e,t,i,r)}function maybeEval(n,e,t){return htmx.config.allowEval?e():(triggerErrorEvent(n,"htmx:evalDisallowedError"),t)}function getHXVarsForElement(n,e,t){return getValuesForElement(n,"hx-vars",!0,t,e)}function getHXValsForElement(n,e,t){return getValuesForElement(n,"hx-vals",!1,t,e)}function getExpressionVars(n,e){return mergeObjects(getHXVarsForElement(n,e),getHXValsForElement(n,e))}function safelySetHeaderValue(n,e,t){if(t!==null)try{n.setRequestHeader(e,t)}catch(i){n.setRequestHeader(e,encodeURIComponent(t)),n.setRequestHeader(e+"-URI-AutoEncoded","true")}}function getPathFromResponse(n){if(n.responseURL)try{let e=new URL(n.responseURL);return e.pathname+e.search}catch(e){triggerErrorEvent(getDocument().body,"htmx:badResponseUrl",{url:n.responseURL})}}function hasHeader(n,e){return e.test(n.getAllResponseHeaders())}function ajaxHelper(n,e,t){if(n=n.toLowerCase(),t){if(t instanceof Element||typeof t=="string")return issueAjaxRequest(n,e,null,null,{targetOverride:resolveTarget(t)||DUMMY_ELT,returnPromise:!0});{let i=resolveTarget(t.target);return(t.target&&!i||t.source&&!i&&!resolveTarget(t.source))&&(i=DUMMY_ELT),issueAjaxRequest(n,e,resolveTarget(t.source),t.event,{handler:t.handler,headers:t.headers,values:t.values,targetOverride:i,swapOverride:t.swap,select:t.select,returnPromise:!0,push:t.push,replace:t.replace,selectOOB:t.selectOOB})}}else return issueAjaxRequest(n,e,null,null,{returnPromise:!0})}function hierarchyForElt(n){let e=[];for(;n;)e.push(n),n=n.parentElement;return e}function verifyPath(n,e,t){let i=new URL(e,location.protocol!=="about:"?location.href:window.origin),o=(location.protocol!=="about:"?location.origin:window.origin)===i.origin;return htmx.config.selfRequestsOnly&&!o?!1:triggerEvent(n,"htmx:validateUrl",mergeObjects({url:i,sameHost:o},t))}function formDataFromObject(n){if(n instanceof FormData)return n;let e=new FormData;for(let t in n)n.hasOwnProperty(t)&&(n[t]&&typeof n[t].forEach=="function"?n[t].forEach(function(i){e.append(t,i)}):typeof n[t]=="object"&&!(n[t]instanceof Blob)?e.append(t,JSON.stringify(n[t])):e.append(t,n[t]));return e}function formDataArrayProxy(n,e,t){return new Proxy(t,{get:function(i,r){return typeof r=="number"?i[r]:r==="length"?i.length:r==="push"?function(o){i.push(o),n.append(e,o)}:typeof i[r]=="function"?function(){i[r].apply(i,arguments),n.delete(e),i.forEach(function(o){n.append(e,o)})}:i[r]&&i[r].length===1?i[r][0]:i[r]},set:function(i,r,o){return i[r]=o,n.delete(e),i.forEach(function(s){n.append(e,s)}),!0}})}function formDataProxy(n){return new Proxy(n,{get:function(e,t){if(typeof t=="symbol"){let r=Reflect.get(e,t);return typeof r=="function"?function(){return r.apply(n,arguments)}:r}if(t==="toJSON")return()=>Object.fromEntries(n);if(t in e&&typeof e[t]=="function")return function(){return n[t].apply(n,arguments)};let i=n.getAll(t);if(i.length!==0)return i.length===1?i[0]:formDataArrayProxy(e,t,i)},set:function(e,t,i){return typeof t!="string"?!1:(e.delete(t),i&&typeof i.forEach=="function"?i.forEach(function(r){e.append(t,r)}):typeof i=="object"&&!(i instanceof Blob)?e.append(t,JSON.stringify(i)):e.append(t,i),!0)},deleteProperty:function(e,t){return typeof t=="string"&&e.delete(t),!0},ownKeys:function(e){return Reflect.ownKeys(Object.fromEntries(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e),t)}})}function issueAjaxRequest(n,e,t,i,r,o){let s=null,a=null;if(r=r!=null?r:{},r.returnPromise&&typeof Promise!="undefined")var l=new Promise(function(Y,ee){s=Y,a=ee});t==null&&(t=getDocument().body);let c=r.handler||handleAjaxResponse,u=r.select||null;if(!bodyContains(t))return maybeCall(s),l;let d=r.targetOverride||asElement(getTarget(t));if(d==null||d==DUMMY_ELT)return triggerErrorEvent(t,"htmx:targetError",{target:getClosestAttributeValue(t,"hx-target")}),maybeCall(a),l;let p=getInternalData(t),y=p.lastButtonClicked;if(y){let Y=getRawAttribute(y,"formaction");Y!=null&&(e=Y);let ee=getRawAttribute(y,"formmethod");if(ee!=null)if(VERBS.includes(ee.toLowerCase()))n=ee;else return maybeCall(s),l}let m=getClosestAttributeValue(t,"hx-confirm");if(o===void 0&&triggerEvent(t,"htmx:confirm",{target:d,elt:t,path:e,verb:n,triggeringEvent:i,etc:r,issueRequest:function(oe){return issueAjaxRequest(n,e,t,i,r,!!oe)},question:m})===!1)return maybeCall(s),l;let g=t,_=getClosestAttributeValue(t,"hx-sync"),C=null,O=!1;if(_){let Y=_.split(":"),ee=Y[0].trim();if(ee==="this"?g=findThisElement(t,"hx-sync"):g=asElement(querySelectorExt(t,ee)),_=(Y[1]||"drop").trim(),p=getInternalData(g),_==="drop"&&p.xhr&&p.abortable!==!0)return maybeCall(s),l;if(_==="abort"){if(p.xhr)return maybeCall(s),l;O=!0}else _==="replace"?triggerEvent(g,"htmx:abort"):_.indexOf("queue")===0&&(C=(_.split(" ")[1]||"last").trim())}if(p.xhr)if(p.abortable)triggerEvent(g,"htmx:abort");else{if(C==null){if(i){let Y=getInternalData(i);Y&&Y.triggerSpec&&Y.triggerSpec.queue&&(C=Y.triggerSpec.queue)}C==null&&(C="last")}return p.queuedRequests==null&&(p.queuedRequests=[]),C==="first"&&p.queuedRequests.length===0?p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)}):C==="all"?p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)}):C==="last"&&(p.queuedRequests=[],p.queuedRequests.push(function(){issueAjaxRequest(n,e,t,i,r)})),maybeCall(s),l}let w=new XMLHttpRequest;p.xhr=w,p.abortable=O;let T=function(){p.xhr=null,p.abortable=!1,p.queuedRequests!=null&&p.queuedRequests.length>0&&p.queuedRequests.shift()()},$=getClosestAttributeValue(t,"hx-prompt");if($){var j=prompt($);if(j===null||!triggerEvent(t,"htmx:prompt",{prompt:j,target:d}))return maybeCall(s),T(),l}if(m&&!o&&!confirm(m))return maybeCall(s),T(),l;let H=getHeaders(t,d,j);n!=="get"&&!usesFormData(t)&&(H["Content-Type"]="application/x-www-form-urlencoded"),r.headers&&(H=mergeObjects(H,r.headers));let I=getInputValues(t,n),L=I.errors,W=I.formData;r.values&&overrideFormData(W,formDataFromObject(r.values));let G=formDataFromObject(getExpressionVars(t,i)),Q=overrideFormData(W,G),Z=filterValues(Q,t);htmx.config.getCacheBusterParam&&n==="get"&&Z.set("org.htmx.cache-buster",getRawAttribute(d,"id")||"true"),(e==null||e==="")&&(e=location.href);let he=getValuesForElement(t,"hx-request"),Ce=getInternalData(t).boosted,ie=htmx.config.methodsThatUseUrlParams.indexOf(n)>=0,ne={boosted:Ce,useUrlParams:ie,formData:Z,parameters:formDataProxy(Z),unfilteredFormData:Q,unfilteredParameters:formDataProxy(Q),headers:H,elt:t,target:d,verb:n,errors:L,withCredentials:r.credentials||he.credentials||htmx.config.withCredentials,timeout:r.timeout||he.timeout||htmx.config.timeout,path:e,triggeringEvent:i};if(!triggerEvent(t,"htmx:configRequest",ne))return maybeCall(s),T(),l;if(e=ne.path,n=ne.verb,H=ne.headers,Z=formDataFromObject(ne.parameters),L=ne.errors,ie=ne.useUrlParams,L&&L.length>0)return triggerEvent(t,"htmx:validation:halted",ne),maybeCall(s),T(),l;let Ue=e.split("#"),Re=Ue[0],q=Ue[1],M=e;if(ie&&(M=Re,!Z.keys().next().done&&(M.indexOf("?")<0?M+="?":M+="&",M+=urlEncode(Z),q&&(M+="#"+q))),!verifyPath(t,M,ne))return triggerErrorEvent(t,"htmx:invalidPath",ne),maybeCall(a),T(),l;if(w.open(n.toUpperCase(),M,!0),w.overrideMimeType("text/html"),w.withCredentials=ne.withCredentials,w.timeout=ne.timeout,!he.noHeaders){for(let Y in H)if(H.hasOwnProperty(Y)){let ee=H[Y];safelySetHeaderValue(w,Y,ee)}}let A={xhr:w,target:d,requestConfig:ne,etc:r,boosted:Ce,select:u,pathInfo:{requestPath:e,finalRequestPath:M,responsePath:null,anchor:q}};if(w.onload=function(){try{let Y=hierarchyForElt(t);if(A.pathInfo.responsePath=getPathFromResponse(w),c(t,A),A.keepIndicators!==!0&&removeRequestIndicators(B,V),triggerEvent(t,"htmx:afterRequest",A),triggerEvent(t,"htmx:afterOnLoad",A),!bodyContains(t)){let ee=null;for(;Y.length>0&&ee==null;){let oe=Y.shift();bodyContains(oe)&&(ee=oe)}ee&&(triggerEvent(ee,"htmx:afterRequest",A),triggerEvent(ee,"htmx:afterOnLoad",A))}maybeCall(s)}catch(Y){throw triggerErrorEvent(t,"htmx:onLoadError",mergeObjects({error:Y},A)),Y}finally{T()}},w.onerror=function(){removeRequestIndicators(B,V),triggerErrorEvent(t,"htmx:afterRequest",A),triggerErrorEvent(t,"htmx:sendError",A),maybeCall(a),T()},w.onabort=function(){removeRequestIndicators(B,V),triggerErrorEvent(t,"htmx:afterRequest",A),triggerErrorEvent(t,"htmx:sendAbort",A),maybeCall(a),T()},w.ontimeout=function(){removeRequestIndicators(B,V),triggerErrorEvent(t,"htmx:afterRequest",A),triggerErrorEvent(t,"htmx:timeout",A),maybeCall(a),T()},!triggerEvent(t,"htmx:beforeRequest",A))return maybeCall(s),T(),l;var B=addRequestIndicatorClasses(t),V=disableElements(t);forEach(["loadstart","loadend","progress","abort"],function(Y){forEach([w,w.upload],function(ee){ee.addEventListener(Y,function(oe){triggerEvent(t,"htmx:xhr:"+Y,{lengthComputable:oe.lengthComputable,loaded:oe.loaded,total:oe.total})})})}),triggerEvent(t,"htmx:beforeSend",A);let U=ie?null:encodeParamsForBody(w,t,Z);return w.send(U),l}function determineHistoryUpdates(n,e){let t=e.xhr,i=null,r=null;if(hasHeader(t,/HX-Push:/i)?(i=t.getResponseHeader("HX-Push"),r="push"):hasHeader(t,/HX-Push-Url:/i)?(i=t.getResponseHeader("HX-Push-Url"),r="push"):hasHeader(t,/HX-Replace-Url:/i)&&(i=t.getResponseHeader("HX-Replace-Url"),r="replace"),i)return i==="false"?{}:{type:r,path:i};let o=e.pathInfo.finalRequestPath,s=e.pathInfo.responsePath,a=e.etc.push||getClosestAttributeValue(n,"hx-push-url"),l=e.etc.replace||getClosestAttributeValue(n,"hx-replace-url");a==="false"&&(a=null),l==="false"&&(l=null);let c=getInternalData(n).boosted,u=null,d=null;return a?(u="push",d=a):l?(u="replace",d=l):c&&(u="push",d=s||o),d?(d==="true"&&(d=s||o),e.pathInfo.anchor&&d.indexOf("#")===-1&&(d=d+"#"+e.pathInfo.anchor),{type:u,path:d}):{}}function codeMatches(n,e){var t=new RegExp(n.code);return t.test(e.toString(10))}function resolveResponseHandling(n){for(var e=0;e.${e}{opacity:0;visibility: hidden} .${t} .${e}, .${t}.${e}{opacity:1;visibility: visible;transition: opacity 200ms ease-in}`)}}function getMetaConfig(){let n=getDocument().querySelector('meta[name="htmx-config"]');return n?parseJSON(n.content):null}function mergeMetaConfig(){let n=getMetaConfig();n&&(htmx.config=mergeObjects(htmx.config,n))}return ready(function(){mergeMetaConfig(),insertIndicatorStyles();let n=getDocument().body;processNode(n);let e=getDocument().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");n.addEventListener("htmx:abort",function(i){let r=i.detail.elt||i.target,o=getInternalData(r);o&&o.xhr&&o.xhr.abort()});let t=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(i){i.state&&i.state.htmx?(restoreHistory(),forEach(e,function(r){triggerEvent(r,"htmx:restored",{document:getDocument(),triggerEvent})})):t&&t(i)},getWindow().setTimeout(function(){triggerEvent(n,"htmx:load",{}),n=null},0)}),htmx})(),I_=Qp;function Mo(n,e){n.split(/\s+/).forEach(t=>{e(t)})}var ni=class{constructor(){this._events={}}on(e,t){Mo(e,i=>{let r=this._events[i]||[];r.push(t),this._events[i]=r})}off(e,t){var i=arguments.length;if(i===0){this._events={};return}Mo(e,r=>{if(i===1){delete this._events[r];return}let o=this._events[r];o!==void 0&&(o.splice(o.indexOf(t),1),this._events[r]=o)})}trigger(e,...t){var i=this;Mo(e,r=>{let o=i._events[r];o!==void 0&&o.forEach(s=>{s.apply(i,t)})})}};function No(n){return n.plugins={},class extends n{constructor(){super(...arguments),this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(e,t){n.plugins[e]={name:e,fn:t}}initializePlugins(e){var t,i;let r=this,o=[];if(Array.isArray(e))e.forEach(s=>{typeof s=="string"?o.push(s):(r.plugins.settings[s.name]=s.options,o.push(s.name))});else if(e)for(t in e)e.hasOwnProperty(t)&&(r.plugins.settings[t]=e[t],o.push(t));for(;i=o.shift();)r.require(i)}loadPlugin(e){var t=this,i=t.plugins,r=n.plugins[e];if(!n.plugins.hasOwnProperty(e))throw new Error('Unable to find "'+e+'" plugin');i.requested[e]=!0,i.loaded[e]=r.fn.apply(t,[t.plugins.settings[e]||{}]),i.names.push(e)}require(e){var t=this,i=t.plugins;if(!t.plugins.loaded.hasOwnProperty(e)){if(i.requested[e])throw new Error('Plugin has circular dependency ("'+e+'")');t.loadPlugin(e)}return i.loaded[e]}}}var ii=n=>(n=n.filter(Boolean),n.length<2?n[0]||"":Zp(n)==1?"["+n.join("")+"]":"(?:"+n.join("|")+")"),ko=n=>{if(!Jp(n))return n.join("");let e="",t=0,i=()=>{t>1&&(e+="{"+t+"}")};return n.forEach((r,o)=>{if(r===n[o-1]){t++;return}i(),e+=r,t=1}),i(),e},Ho=n=>{let e=Array.from(n);return ii(e)},Jp=n=>new Set(n).size!==n.length,Lt=n=>(n+"").replace(/([\$\(\)\*\+\.\?\[\]\^\{\|\}\\])/gu,"\\$1"),Zp=n=>n.reduce((e,t)=>Math.max(e,em(t)),0),em=n=>Array.from(n).length;var Io=n=>{if(n.length===1)return[[n]];let e=[],t=n.substring(1);return Io(t).forEach(function(r){let o=r.slice(0);o[0]=n.charAt(0)+o[0],e.push(o),o=r.slice(0),o.unshift(n.charAt(0)),e.push(o)}),e};var tm=[[0,65535]],nm="[\u0300-\u036F\xB7\u02BE\u02BC]",nr,Ba,im=3,Ro={},Pa={"/":"\u2044\u2215",0:"\u07C0",a:"\u2C65\u0250\u0251",aa:"\uA733",ae:"\xE6\u01FD\u01E3",ao:"\uA735",au:"\uA737",av:"\uA739\uA73B",ay:"\uA73D",b:"\u0180\u0253\u0183",c:"\uA73F\u0188\u023C\u2184",d:"\u0111\u0257\u0256\u1D05\u018C\uABB7\u0501\u0266",e:"\u025B\u01DD\u1D07\u0247",f:"\uA77C\u0192",g:"\u01E5\u0260\uA7A1\u1D79\uA77F\u0262",h:"\u0127\u2C68\u2C76\u0265",i:"\u0268\u0131",j:"\u0249\u0237",k:"\u0199\u2C6A\uA741\uA743\uA745\uA7A3",l:"\u0142\u019A\u026B\u2C61\uA749\uA747\uA781\u026D",m:"\u0271\u026F\u03FB",n:"\uA7A5\u019E\u0272\uA791\u1D0E\u043B\u0509",o:"\xF8\u01FF\u0254\u0275\uA74B\uA74D\u1D11",oe:"\u0153",oi:"\u01A3",oo:"\uA74F",ou:"\u0223",p:"\u01A5\u1D7D\uA751\uA753\uA755\u03C1",q:"\uA757\uA759\u024B",r:"\u024D\u027D\uA75B\uA7A7\uA783",s:"\xDF\u023F\uA7A9\uA785\u0282",t:"\u0167\u01AD\u0288\u2C66\uA787",th:"\xFE",tz:"\uA729",u:"\u0289",v:"\u028B\uA75F\u028C",vy:"\uA761",w:"\u2C73",y:"\u01B4\u024F\u1EFF",z:"\u01B6\u0225\u0240\u2C6C\uA763",hv:"\u0195"};for(let n in Pa){let e=Pa[n]||"";for(let t=0;t{nr===void 0&&(nr=cm(n||tm))},Fa=(n,e="NFKD")=>n.normalize(e),ri=n=>Array.from(n).reduce((e,t)=>e+sm(t),""),sm=n=>(n=Fa(n).toLowerCase().replace(rm,e=>Ro[e]||""),Fa(n,"NFC"));function*am(n){for(let[e,t]of n)for(let i=e;i<=t;i++){let r=String.fromCharCode(i),o=ri(r);o!=r.toLowerCase()&&(o.length>im||o.length!=0&&(yield{folded:o,composed:r,code_point:i}))}}var lm=n=>{let e={},t=(i,r)=>{let o=e[i]||new Set,s=new RegExp("^"+Ho(o)+"$","iu");r.match(s)||(o.add(Lt(r)),e[i]=o)};for(let i of am(n))t(i.folded,i.folded),t(i.folded,i.composed);return e},cm=n=>{let e=lm(n),t={},i=[];for(let o in e){let s=e[o];s&&(t[o]=Ho(s)),o.length>1&&i.push(Lt(o))}i.sort((o,s)=>s.length-o.length);let r=ii(i);return Ba=new RegExp("^"+r,"u"),t},um=(n,e=1)=>{let t=0;return n=n.map(i=>(nr[i]&&(t+=i.length),nr[i]||i)),t>=e?ko(n):""},dm=(n,e=1)=>(e=Math.max(e,n.length-1),ii(Io(n).map(t=>um(t,e)))),$a=(n,e=!0)=>{let t=n.length>1?1:0;return ii(n.map(i=>{let r=[],o=e?i.length():i.length()-1;for(let s=0;s{for(let t of e){if(t.start!=n.start||t.end!=n.end||t.substrs.join("")!==n.substrs.join(""))continue;let i=n.parts,r=s=>{for(let a of i){if(a.start===s.start&&a.substr===s.substr)return!1;if(!(s.length==1||a.length==1)&&(s.starta.start||a.starts.start))return!0}return!1};if(!(t.parts.filter(r).length>0))return!0}return!1},ir=class n{constructor(){le(this,"parts");le(this,"substrs");le(this,"start");le(this,"end");this.parts=[],this.substrs=[],this.start=0,this.end=0}add(e){e&&(this.parts.push(e),this.substrs.push(e.substr),this.start=Math.min(e.start,this.start),this.end=Math.max(e.end,this.end))}last(){return this.parts[this.parts.length-1]}length(){return this.parts.length}clone(e,t){let i=new n,r=JSON.parse(JSON.stringify(this.parts)),o=r.pop();for(let l of r)i.add(l);let s=t.substr.substring(0,e-o.start),a=s.length;return i.add({start:o.start,end:o.start+a,length:a,substr:s}),i}},Va=n=>{om(),n=ri(n);let e="",t=[new ir];for(let i=0;i0){l=l.sort((u,d)=>u.length()-d.length());for(let u of l)fm(u,t)||t.push(u);continue}if(i>0&&c.size==1&&!c.has("3")){e+=$a(t,!1);let u=new ir,d=t[0];d&&u.add(d.last()),t=[u]}}return e+=$a(t,!0),e};var za=(n,e)=>{if(n)return n[e]},ja=(n,e)=>{if(n){for(var t,i=e.split(".");(t=i.shift())&&(n=n[t]););return n}},rr=(n,e,t)=>{var i,r;return!n||(n=n+"",e.regex==null)||(r=n.search(e.regex),r===-1)?0:(i=e.string.length/n.length,r===0&&(i+=.5),i*t)},or=(n,e)=>{var t=n[e];if(typeof t=="function")return t;t&&!Array.isArray(t)&&(n[e]=[t])},oi=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Wa=(n,e)=>typeof n=="number"&&typeof e=="number"?n>e?1:ne?1:e>n?-1:0);var sr=class{constructor(e,t){le(this,"items");le(this,"settings");this.items=e,this.settings=t||{diacritics:!0}}tokenize(e,t,i){if(!e||!e.length)return[];let r=[],o=e.split(/\s+/);var s;return i&&(s=new RegExp("^("+Object.keys(i).map(Lt).join("|")+"):(.*)$")),o.forEach(a=>{let l,c=null,u=null;s&&(l=a.match(s))&&(c=l[1],a=l[2]),a.length>0&&(this.settings.diacritics?u=Va(a)||null:u=Lt(a),u&&t&&(u="\\b"+u)),r.push({string:a,regex:u?new RegExp(u,"iu"):null,field:c})}),r}getScoreFunction(e,t){var i=this.prepareSearch(e,t);return this._getScoreFunction(i)}_getScoreFunction(e){let t=e.tokens,i=t.length;if(!i)return function(){return 0};let r=e.options.fields,o=e.weights,s=r.length,a=e.getAttrFn;if(!s)return function(){return 1};let l=(function(){return s===1?function(c,u){let d=r[0].field;return rr(a(u,d),c,o[d]||1)}:function(c,u){var d=0;if(c.field){let p=a(u,c.field);!c.regex&&p?d+=1/s:d+=rr(p,c,1)}else oi(o,(p,y)=>{d+=rr(a(u,y),c,p)});return d/s}})();return i===1?function(c){return l(t[0],c)}:e.options.conjunction==="and"?function(c){var u,d=0;for(let p of t){if(u=l(p,c),u<=0)return 0;d+=u}return d/i}:function(c){var u=0;return oi(t,d=>{u+=l(d,c)}),u/i}}getSortFunction(e,t){var i=this.prepareSearch(e,t);return this._getSortFunction(i)}_getSortFunction(e){var t,i=[];let r=this,o=e.options,s=!e.query&&o.sort_empty?o.sort_empty:o.sort;if(typeof s=="function")return s.bind(this);let a=function(c,u){return c==="$score"?u.score:e.getAttrFn(r.items[u.id],c)};if(s)for(let c of s)(e.query||c.field!=="$score")&&i.push(c);if(e.query){t=!0;for(let c of i)if(c.field==="$score"){t=!1;break}t&&i.unshift({field:"$score",direction:"desc"})}else i=i.filter(c=>c.field!=="$score");return i.length?function(c,u){var d,p;for(let y of i)if(p=y.field,d=(y.direction==="desc"?-1:1)*Wa(a(p,c),a(p,u)),d)return d;return 0}:null}prepareSearch(e,t){let i={};var r=Object.assign({},t);if(or(r,"sort"),or(r,"sort_empty"),r.fields){or(r,"fields");let o=[];r.fields.forEach(s=>{typeof s=="string"&&(s={field:s,weight:1}),o.push(s),i[s.field]="weight"in s?s.weight:1}),r.fields=o}return{options:r,query:e.toLowerCase().trim(),tokens:this.tokenize(e,r.respect_word_boundaries,i),total:0,items:[],weights:i,getAttrFn:r.nesting?ja:za}}search(e,t){var i=this,r,o;o=this.prepareSearch(e,t),t=o.options,e=o.query;let s=t.score||i._getScoreFunction(o);e.length?oi(i.items,(l,c)=>{r=s(l),(t.filter===!1||r>0)&&o.items.push({score:r,id:c})}):oi(i.items,(l,c)=>{o.items.push({score:1,id:c})});let a=i._getSortFunction(o);return a&&o.items.sort(a),o.total=o.items.length,typeof t.limit=="number"&&(o.items=o.items.slice(0,t.limit)),o}};var Fe=n=>typeof n=="undefined"||n===null?null:si(n),si=n=>typeof n=="boolean"?n?"1":"0":n+"",ar=n=>(n+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),qa=(n,e)=>e>0?window.setTimeout(n,e):(n.call(null),null),Ua=(n,e)=>{var t;return function(i,r){var o=this;t&&(o.loading=Math.max(o.loading-1,0),clearTimeout(t)),t=setTimeout(function(){t=null,o.loadedSearches[i]=!0,n.call(o,i,r)},e)}},Po=(n,e,t)=>{var i,r=n.trigger,o={};n.trigger=function(){var s=arguments[0];if(e.indexOf(s)!==-1)o[s]=arguments;else return r.apply(n,arguments)},t.apply(n,[]),n.trigger=r;for(i of e)i in o&&r.apply(n,o[i])},Ya=n=>({start:n.selectionStart||0,length:(n.selectionEnd||0)-(n.selectionStart||0)}),fe=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},De=(n,e,t,i)=>{n.addEventListener(e,t,i)},Mt=(n,e)=>{if(!e||!e[n])return!1;var t=(e.altKey?1:0)+(e.ctrlKey?1:0)+(e.shiftKey?1:0)+(e.metaKey?1:0);return t===1},lr=(n,e)=>{let t=n.getAttribute("id");return t||(n.setAttribute("id",e),e)},Fo=n=>n.replace(/[\\"']/g,"\\$&"),Nt=(n,e)=>{e&&n.append(e)},ve=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)};var nt=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if($o(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},$o=n=>typeof n=="string"&&n.indexOf("<")>-1,Ga=n=>n.replace(/['"\\]/g,"\\$&"),cr=(n,e)=>{var t=document.createEvent("HTMLEvents");t.initEvent(e,!0,!1),n.dispatchEvent(t)},ai=(n,e)=>{Object.assign(n.style,e)},$e=(n,...e)=>{var t=Ka(e);n=Xa(n),n.map(i=>{t.map(r=>{i.classList.add(r)})})},gt=(n,...e)=>{var t=Ka(e);n=Xa(n),n.map(i=>{t.map(r=>{i.classList.remove(r)})})},Ka=n=>{var e=[];return ve(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},Xa=n=>(Array.isArray(n)||(n=[n]),n),ur=(n,e,t)=>{if(!(t&&!t.contains(n)))for(;n&&n.matches;){if(n.matches(e))return n;n=n.parentNode}},Bo=(n,e=0)=>e>0?n[n.length-1]:n[0],Qa=n=>Object.keys(n).length===0,Vo=(n,e)=>{if(!n)return-1;e=e||n.nodeName;for(var t=0;n=n.previousElementSibling;)n.matches(e)&&t++;return t},ae=(n,e)=>{ve(e,(t,i)=>{t==null?n.removeAttribute(i):n.setAttribute(i,""+t)})},li=(n,e)=>{n.parentNode&&n.parentNode.replaceChild(e,n)};var Ja=(n,e)=>{if(e===null)return;if(typeof e=="string"){if(!e.length)return;e=new RegExp(e,"i")}let t=o=>{var s=o.data.match(e);if(s&&o.data.length>0){var a=document.createElement("span");a.className="highlight";var l=o.splitText(s.index);l.splitText(s[0].length);var c=l.cloneNode(!0);return a.appendChild(c),li(l,a),1}return 0},i=o=>{o.nodeType===1&&o.childNodes&&!/(script|style)/i.test(o.tagName)&&(o.className!=="highlight"||o.tagName!=="SPAN")&&Array.from(o.childNodes).forEach(s=>{r(s)})},r=o=>o.nodeType===3?t(o):(i(o),0);r(n)},Za=n=>{var e=n.querySelectorAll("span.highlight");Array.prototype.forEach.call(e,function(t){var i=t.parentNode;i.replaceChild(t.firstChild,t),i.normalize()})};var hm=typeof navigator=="undefined"?!1:/Mac/.test(navigator.userAgent),ci=hm?"metaKey":"ctrlKey";var zo={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:null,createOnBlur:!1,createFilter:null,clearAfterSelect:!1,highlight:!0,openOnFocus:!0,shouldOpen:null,maxOptions:50,maxItems:null,hideSelected:null,duplicates:!1,addPrecedence:!1,selectOnTab:!1,preload:null,allowEmptyOption:!1,refreshThrottle:300,loadThrottle:300,loadingClass:"loading",dataAttr:null,optgroupField:"optgroup",valueField:"value",labelField:"text",disabledField:"disabled",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"ts-wrapper",controlClass:"ts-control",dropdownClass:"ts-dropdown",dropdownContentClass:"ts-dropdown-content",itemClass:"item",optionClass:"option",dropdownParent:null,controlInput:'',copyClassesToDropdown:!1,placeholder:null,hidePlaceholder:null,shouldLoad:function(n){return n.length>0},render:{}};function dr(n,e){var t=Object.assign({},zo,e),i=t.dataAttr,r=t.labelField,o=t.valueField,s=t.disabledField,a=t.optgroupField,l=t.optgroupLabelField,c=t.optgroupValueField,u=n.tagName.toLowerCase(),d=n.getAttribute("placeholder")||n.getAttribute("data-placeholder");if(!d&&!t.allowEmptyOption){let g=n.querySelector('option[value=""]');g&&(d=g.textContent)}var p={placeholder:d,options:[],optgroups:[],items:[],maxItems:null},y=()=>{var g,_=p.options,C={},O=1;let w=0;var T=H=>{var I=Object.assign({},H.dataset),L=i&&I[i];return typeof L=="string"&&L.length&&(I=Object.assign(I,JSON.parse(L))),I},$=(H,I)=>{var L=Fe(H.value);if(L!=null&&!(!L&&!t.allowEmptyOption)){if(C.hasOwnProperty(L)){if(I){var W=C[L][a];W?Array.isArray(W)?W.push(I):C[L][a]=[W,I]:C[L][a]=I}}else{var G=T(H);G[r]=G[r]||H.textContent,G[o]=G[o]||L,G[s]=G[s]||H.disabled,G[a]=G[a]||I,G.$option=H,G.$order=G.$order||++w,C[L]=G,_.push(G)}H.selected&&p.items.push(L)}},j=H=>{var I,L;L=T(H),L[l]=L[l]||H.getAttribute("label")||"",L[c]=L[c]||O++,L[s]=L[s]||H.disabled,L.$order=L.$order||++w,p.optgroups.push(L),I=L[c],ve(H.children,W=>{$(W,I)})};p.maxItems=n.hasAttribute("multiple")?null:1,ve(n.children,H=>{g=H.tagName.toLowerCase(),g==="optgroup"?j(H):g==="option"&&$(H)})},m=()=>{var g,_;let C=n.getAttribute(i);if(C)p.options=JSON.parse(C),ve(p.options,w=>{p.items.push(w[o])});else{var O=(_=(g=n==null?void 0:n.value)===null||g===void 0?void 0:g.trim())!==null&&_!==void 0?_:"";if(!t.allowEmptyOption&&!O.length)return;let w=O.split(t.delimiter);ve(w,T=>{let $={};$[r]=T,$[o]=T,p.options.push($)}),p.items=w}};return u==="select"?y():m(),Object.assign({},zo,p,e)}var nl=0,we=class extends No(ni){constructor(e,t){super(),this.order=0,this.isOpen=!1,this.isDisabled=!1,this.isReadOnly=!1,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.isDropdownContentStale=!0,this.ignoreFocus=!1,this.ignoreHover=!1,this.hasOptions=!1,this.lastValue="",this.caretPos=0,this.loading=0,this.loadedSearches={},this.activeOption=null,this.activeItems=[],this.optgroups={},this.options={},this.userOptions={},this.items=[],this.refreshTimeout=null,nl++;var i,r=nt(e);if(r.tomselect)throw new Error("Tom Select already initialized on this element");r.tomselect=this;var o=window.getComputedStyle&&window.getComputedStyle(r,null);i=o.getPropertyValue("direction");let s=dr(r,t);this.settings=s,this.input=r,this.tabIndex=r.tabIndex||0,this.is_select_tag=r.tagName.toLowerCase()==="select",this.rtl=/rtl/i.test(i),this.inputId=lr(r,"tomselect-"+nl),this.isRequired=r.required,this.sifter=new sr(this.options,{diacritics:s.diacritics}),s.mode=s.mode||(s.maxItems===1?"single":"multi"),typeof s.hideSelected!="boolean"&&(s.hideSelected=s.mode==="multi"),typeof s.hidePlaceholder!="boolean"&&(s.hidePlaceholder=s.mode!=="multi");var a=s.createFilter;typeof a!="function"&&(typeof a=="string"&&(a=new RegExp(a)),a instanceof RegExp?s.createFilter=_=>a.test(_):s.createFilter=_=>this.settings.duplicates||!this.options[_]),this.initializePlugins(s.plugins),this.setupCallbacks(),this.setupTemplates();let l=nt("
"),c=nt("
"),u=this._render("dropdown"),d=nt('
'),p=this.input.getAttribute("class")||"",y=s.mode;var m;if($e(l,s.wrapperClass,p,y),$e(c,s.controlClass),Nt(l,c),$e(u,s.dropdownClass,y),s.copyClassesToDropdown&&$e(u,p),$e(d,s.dropdownContentClass),Nt(u,d),nt(s.dropdownParent||l).appendChild(u),$o(s.controlInput)){m=nt(s.controlInput);var g=["autocorrect","autocapitalize","autocomplete","spellcheck","aria-label"];ve(g,_=>{r.getAttribute(_)&&ae(m,{[_]:r.getAttribute(_)})}),m.tabIndex=-1,c.appendChild(m),this.focus_node=m}else s.controlInput?(m=nt(s.controlInput),this.focus_node=m):(m=nt(""),this.focus_node=c);this.wrapper=l,this.dropdown=u,this.dropdown_content=d,this.control=c,this.control_input=m,this.setup()}setup(){let e=this,t=e.settings,i=e.control_input,r=e.dropdown,o=e.dropdown_content,s=e.wrapper,a=e.control,l=e.input,c=e.focus_node,u={passive:!0},d=e.inputId+"-ts-dropdown";ae(o,{id:d}),ae(c,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":d});let p=lr(c,e.inputId+"-ts-control"),y="label[for='"+Ga(e.inputId)+"']",m=document.querySelector(y),g=e.focus.bind(e);if(m){De(m,"click",g),ae(m,{for:p});let w=lr(m,e.inputId+"-ts-label");ae(c,{"aria-labelledby":w}),ae(o,{"aria-labelledby":w})}if(s.style.width=l.style.width,s.style.minWidth=l.style.minWidth,s.style.maxWidth=l.style.maxWidth,e.plugins.names.length){let w="plugin-"+e.plugins.names.join(" plugin-");$e([s,r],w)}(t.maxItems===null||t.maxItems>1)&&e.is_select_tag&&ae(l,{multiple:"multiple"}),t.placeholder&&ae(i,{placeholder:t.placeholder}),!t.splitOn&&t.delimiter&&(t.splitOn=new RegExp("\\s*"+Lt(t.delimiter)+"+\\s*")),t.load&&t.loadThrottle&&(t.load=Ua(t.load,t.loadThrottle)),De(r,"mousemove",()=>{e.ignoreHover=!1}),De(r,"mouseenter",w=>{var T=ur(w.target,"[data-selectable]",r);T&&e.onOptionHover(w,T)},{capture:!0}),De(r,"click",w=>{let T=ur(w.target,"[data-selectable]");T&&(e.onOptionSelect(w,T),fe(w,!0))}),De(a,"click",w=>{var T=ur(w.target,"[data-ts-item]",a);if(T&&e.onItemSelect(w,T)){fe(w,!0);return}i.value==""&&(e.onClick(),fe(w,!0))}),De(c,"keydown",w=>e.onKeyDown(w)),De(i,"keypress",w=>e.onKeyPress(w)),De(i,"input",w=>e.onInput(w)),De(c,"blur",w=>e.onBlur(w)),De(c,"focus",w=>e.onFocus(w)),De(i,"paste",w=>e.onPaste(w));let _=w=>{let T=w.composedPath()[0];if(!s.contains(T)&&!r.contains(T)){e.isFocused&&e.blur(),e.inputState();return}T==i&&e.isOpen?w.stopPropagation():fe(w,!0)},C=()=>{e.isOpen&&e.positionDropdown()},O=()=>{e.isValid&&(e.isValid=!1,e.isInvalid=!0,e.refreshState())};De(l,"invalid",O),De(document,"mousedown",_),De(window,"scroll",C,u),De(window,"resize",C,u),this._destroy=()=>{l.removeEventListener("invalid",O),document.removeEventListener("mousedown",_),window.removeEventListener("scroll",C),window.removeEventListener("resize",C),m&&m.removeEventListener("click",g)},this.revertSettings={innerHTML:l.innerHTML,tabIndex:l.tabIndex},l.tabIndex=-1,l.insertAdjacentElement("afterend",e.wrapper),e.sync(!1),t.items=[],delete t.optgroups,delete t.options,e.refreshItems(),e.close(!1),e.inputState(),e.isSetup=!0,e.on("change",this.onChange),$e(l,"tomselected","ts-hidden-accessible"),e.trigger("initialize"),t.preload===!0&&e.preload()}setupOptions(e=[],t=[]){this.addOptions(e),ve(t,i=>{this.registerOptionGroup(i)})}setupTemplates(){var e=this,t=e.settings.labelField,i=e.settings.optgroupLabelField,r={optgroup:o=>{let s=document.createElement("div");return s.className="optgroup",s.appendChild(o.options),s},optgroup_header:(o,s)=>'
'+s(o[i])+"
",option:(o,s)=>"
"+s(o[t])+"
",item:(o,s)=>"
"+s(o[t])+"
",option_create:(o,s)=>'
Add '+s(o.input)+"
",no_results:()=>'
No results found
',loading:()=>'
',not_loading:()=>{},dropdown:()=>"
"};e.settings.render=Object.assign({},r,e.settings.render)}setupCallbacks(){var e,t,i={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",item_select:"onItemSelect",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(e in i)t=this.settings[i[e]],t&&this.on(e,t)}sync(e=!0){let t=this,i=e?dr(t.input,{delimiter:t.settings.delimiter,allowEmptyOption:t.settings.allowEmptyOption}):t.settings;t.setupOptions(i.options,i.optgroups),t.setValue(i.items||[],!0),t.input.disabled?t.disable():t.input.readOnly?t.setReadOnly(!0):t.enable(),t.lastQuery=null}onClick(){var e=this;if(e.activeItems.length>0){e.clearActiveItems(),e.focus();return}e.isFocused&&e.isOpen?e.blur():e.focus()}onMouseDown(){}onChange(){cr(this.input,"input"),cr(this.input,"change")}onPaste(e){var t=this;if(t.isInputHidden||t.isLocked){fe(e);return}t.settings.splitOn&&setTimeout(()=>{var i=t.inputValue();if(i.match(t.settings.splitOn)){var r=i.trim().split(t.settings.splitOn);ve(r,o=>{Fe(o)&&(this.options[o]?t.addItem(o):t.createItem(o))})}},0)}onKeyPress(e){var t=this;if(t.isLocked){fe(e);return}var i=String.fromCharCode(e.keyCode||e.which);if(t.settings.create&&t.settings.mode==="multi"&&i===t.settings.delimiter){t.createItem(),fe(e);return}}onKeyDown(e){var t=this;if(t.ignoreHover=!0,t.isLocked){e.keyCode!==9&&fe(e);return}switch(e.keyCode){case 65:if(Mt(ci,e)&&t.control_input.value==""){fe(e),t.selectAll();return}break;case 27:t.isOpen&&(fe(e,!0),t.close()),t.clearActiveItems();return;case 40:if(!t.isOpen&&t.hasOptions)t.open();else if(t.activeOption){let i=t.getAdjacent(t.activeOption,1);i&&t.setActiveOption(i)}fe(e);return;case 38:if(t.activeOption){let i=t.getAdjacent(t.activeOption,-1);i&&t.setActiveOption(i)}fe(e);return;case 13:t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),fe(e)):t.settings.create&&t.createItem()?fe(e):document.activeElement==t.control_input&&t.isOpen&&fe(e);return;case 37:t.advanceSelection(-1,e);return;case 39:t.advanceSelection(1,e);return;case 9:t.settings.selectOnTab&&(t.canSelect(t.activeOption)?(t.onOptionSelect(e,t.activeOption),fe(e)):t.settings.create&&t.createItem()&&fe(e));return;case 8:case 46:t.deleteSelection(e);return}t.isInputHidden&&!Mt(ci,e)&&fe(e)}onInput(e){if(this.isLocked)return;let t=this.inputValue();if(this.lastValue!==t){if(this.lastValue=t,t==""){this._onInput();return}this.refreshTimeout&&window.clearTimeout(this.refreshTimeout),this.refreshTimeout=qa(()=>{this.refreshTimeout=null,this._onInput()},this.settings.refreshThrottle)}}_onInput(){let e=this.lastValue;this.settings.shouldLoad.call(this,e)&&this.load(e),this.refreshOptions(),this.trigger("type",e)}onOptionHover(e,t){this.ignoreHover||this.setActiveOption(t,!1)}onFocus(e){var t=this,i=t.isFocused;if(t.isDisabled||t.isReadOnly){t.blur(),fe(e);return}t.ignoreFocus||(t.isFocused=!0,t.settings.preload==="focus"&&t.preload(),i||t.trigger("focus"),t.activeItems.length||(t.inputState(),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())}onBlur(e){if(document.hasFocus()!==!1){var t=this;if(t.isFocused){t.isFocused=!1,t.ignoreFocus=!1;var i=()=>{t.close(),t.setActiveItem(),t.setCaret(t.items.length),t.trigger("blur")};t.settings.create&&t.settings.createOnBlur?t.createItem(null,i):i()}}}onOptionSelect(e,t){var i,r=this;t.parentElement&&t.parentElement.matches("[data-disabled]")||(t.classList.contains("create")?r.createItem(null,()=>{r.settings.closeAfterSelect?r.close():r.settings.clearAfterSelect&&r.setTextboxValue()}):(i=t.dataset.value,typeof i!="undefined"&&(r.isDropdownContentStale=r.settings.hideSelected,r.addItem(i),r.settings.closeAfterSelect?r.close():r.settings.clearAfterSelect&&r.setTextboxValue(),!r.settings.hideSelected&&e.type&&/click/.test(e.type)&&r.setActiveOption(t))))}canSelect(e){return!!(this.isOpen&&e&&this.dropdown_content.contains(e))}onItemSelect(e,t){var i=this;return!i.isLocked&&i.settings.mode==="multi"?(fe(e),i.setActiveItem(t,e),!0):!1}canLoad(e){return!(!this.settings.load||this.loadedSearches.hasOwnProperty(e))}load(e){let t=this;if(!t.canLoad(e))return;$e(t.wrapper,t.settings.loadingClass),t.loading++;let i=t.loadCallback.bind(t);t.settings.load.call(t,e,i)}loadCallback(e,t){let i=this;i.loading=Math.max(i.loading-1,0),i.isDropdownContentStale=!0,i.clearActiveOption(),i.setupOptions(e,t),i.refreshOptions(i.isFocused&&!i.isInputHidden),i.loading||gt(i.wrapper,i.settings.loadingClass),i.trigger("load",e,t)}preload(){var e=this.wrapper.classList;e.contains("preloaded")||(e.add("preloaded"),this.load(""))}setTextboxValue(e=""){var t=this.control_input,i=t.value!==e;i&&(t.value=e,cr(t,"update"),this.lastValue=e)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(e,t){var i=t?[]:["change"];Po(this,i,()=>{this.clear(t),this.addItems(e,t)})}setMaxItems(e){e===0&&(e=null),this.settings.maxItems=e,this.refreshState()}setActiveItem(e,t){var i=this,r,o,s,a,l,c;if(i.settings.mode!=="single"){if(!e){i.clearActiveItems(),i.isFocused&&i.inputState();return}if(r=t&&t.type.toLowerCase(),r==="click"&&Mt("shiftKey",t)&&i.activeItems.length){for(c=i.getLastActive(),s=Array.prototype.indexOf.call(i.control.children,c),a=Array.prototype.indexOf.call(i.control.children,e),s>a&&(l=s,s=a,a=l),o=s;o<=a;o++)e=i.control.children[o],i.activeItems.indexOf(e)===-1&&i.setActiveItemClass(e);fe(t)}else r==="click"&&Mt(ci,t)||r==="keydown"&&Mt("shiftKey",t)?e.classList.contains("active")?i.removeActiveItem(e):i.setActiveItemClass(e):(i.clearActiveItems(),i.setActiveItemClass(e));i.inputState(),i.isFocused||i.focus()}}setActiveItemClass(e){let t=this,i=t.control.querySelector(".last-active");i&>(i,"last-active"),$e(e,"active last-active"),t.trigger("item_select",e),t.activeItems.indexOf(e)==-1&&t.activeItems.push(e)}removeActiveItem(e){var t=this.activeItems.indexOf(e);this.activeItems.splice(t,1),gt(e,"active")}clearActiveItems(){gt(this.activeItems,"active"),this.activeItems=[]}setActiveOption(e,t=!0){e!==this.activeOption&&(this.clearActiveOption(),e&&(this.activeOption=e,ae(this.focus_node,{"aria-activedescendant":e.getAttribute("id")}),ae(e,{"aria-selected":"true"}),$e(e,"active"),t&&this.scrollToOption(e)))}scrollToOption(e,t){if(!e)return;let i=this.dropdown_content,r=i.clientHeight,o=i.scrollTop||0,s=e.offsetHeight,a=e.getBoundingClientRect().top-i.getBoundingClientRect().top+o;a+s>r+o?this.scroll(a-r+s,t):a{e.setActiveItemClass(i)}))}inputState(){var e=this;e.control.contains(e.control_input)&&(ae(e.control_input,{placeholder:e.settings.placeholder}),e.activeItems.length>0||!e.isFocused&&e.settings.hidePlaceholder&&e.items.length>0?(e.setTextboxValue(),e.isInputHidden=!0):(e.settings.hidePlaceholder&&e.items.length>0&&ae(e.control_input,{placeholder:""}),e.isInputHidden=!1),e.wrapper.classList.toggle("input-hidden",e.isInputHidden))}inputValue(){return this.control_input.value.trim()}focus(){var e=this;if(e.isDisabled||e.isReadOnly)return;e.ignoreFocus=!0;let t=this.control_input.offsetWidth?this.control_input:this.focus_node;t.focus(),setTimeout(()=>{e.ignoreFocus=!1,t.getRootNode().activeElement===t&&this.onFocus()},0)}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())}getSearchOptions(){var e=this.settings,t=e.sortField;return typeof e.sortField=="string"&&(t=[{field:e.sortField}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}}search(e){var t,i,r=this,o=this.getSearchOptions();if(r.settings.score&&(i=r.settings.score.call(r,e),typeof i!="function"))throw new Error('Tom Select "score" setting must be a function that returns a function');return r.isDropdownContentStale||e!==r.lastQuery?(r.lastQuery=e,/(.)\1{15,}/.test(e)&&(e=""),t=r.sifter.search(e,Object.assign(o,{score:i})),r.currentResults=t):t=Object.assign({},r.currentResults),r.settings.hideSelected&&(t.items=t.items.filter(s=>{let a=Fe(s.id);return!(a!==null&&r.items.indexOf(a)!==-1)})),t}refreshOptions(e=!0){var t,i,r,o,s,a,l,c,u,d;let p={},y=[];var m=this,g=m.inputValue();let _=g===m.lastQuery||g==""&&m.lastQuery==null;var C=m.search(g),O=null,w=m.settings.shouldOpen||!1,T=m.dropdown_content;_&&(O=m.activeOption,O&&(u=O.closest("[data-group]"))),o=C.items.length,typeof m.settings.maxOptions=="number"&&(o=Math.min(o,m.settings.maxOptions)),o>0&&(w=!0);let $=(I,L)=>{let W=p[I];if(W!==void 0){let Q=y[W];if(Q!==void 0)return[W,Q.fragment]}let G=document.createDocumentFragment();return W=y.length,y.push({fragment:G,order:L,optgroup:I}),[W,G]};for(t=0;t0&&(Q=Q.cloneNode(!0),ae(Q,{id:W.$id+"-clone-"+i,"aria-selected":null}),Q.classList.add("ts-cloned"),gt(Q,"active"),m.activeOption&&m.activeOption.dataset.value==L&&u&&u.dataset.group===s.toString()&&(O=Q)),ie.appendChild(Q),s!=""&&(p[s]=Ce)}}m.settings.lockOptgroupOrder&&y.sort((I,L)=>I.order-L.order),l=document.createDocumentFragment(),ve(y,I=>{let L=I.fragment,W=I.optgroup;if(!L||!L.children.length)return;let G=m.optgroups[W];if(G!==void 0){let Q=document.createDocumentFragment(),Z=m.render("optgroup_header",G);Nt(Q,Z),Nt(Q,L);let he=m.render("optgroup",{group:G,options:Q});Nt(l,he)}else Nt(l,L)}),T.innerHTML="",Nt(T,l),m.isDropdownContentStale=!1,m.settings.highlight&&(Za(T),C.query.length&&C.tokens.length&&ve(C.tokens,I=>{Ja(T,I.regex)}));var H=I=>{let L=m.render(I,{input:g});return L&&(w=!0,T.insertBefore(L,T.firstChild)),L};if(m.loading?H("loading"):m.settings.shouldLoad.call(m,g)?C.items.length===0&&H("no_results"):H("not_loading"),c=m.canCreate(g),c&&(d=H("option_create")),m.hasOptions=C.items.length>0||c,w){if(C.items.length>0){if(!O&&m.settings.mode==="single"&&m.items[0]!=null&&(O=m.getOption(m.items[0])),!T.contains(O)){let I=0;d&&!m.settings.addPrecedence&&(I=1),O=m.selectable()[I]}}else d&&(O=d);e&&!m.isOpen&&(m.open(),m.scrollToOption(O,"auto")),m.setActiveOption(O)}else m.clearActiveOption(),e&&m.isOpen&&m.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(e,t=!1){let i=this;if(Array.isArray(e))return i.addOptions(e,t),!1;let r=Fe(e[i.settings.valueField]);return r===null||i.options.hasOwnProperty(r)?(i.updateOption(e[i.settings.valueField],e),!1):(e.$order=e.$order||++i.order,e.$id=i.inputId+"-opt-"+e.$order,i.options[r]=e,i.isDropdownContentStale=!0,t&&(i.userOptions[r]=t,i.trigger("option_add",r,e)),r)}addOptions(e,t=!1){ve(e,i=>{this.addOption(i,t)})}registerOption(e){return this.addOption(e)}registerOptionGroup(e){var t=Fe(e[this.settings.optgroupValueField]);return t===null?!1:(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)}addOptionGroup(e,t){var i;t[this.settings.optgroupValueField]=e,(i=this.registerOptionGroup(t))&&this.trigger("optgroup_add",i,t)}removeOptionGroup(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.clearCache(),this.trigger("optgroup_remove",e))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(e,t){let i=this;var r,o;let s=Fe(e),a=Fe(t[i.settings.valueField]);if(s===null)return;let l=i.options[s];if(l==null)return;if(typeof a!="string")throw new Error("Value must be set in option data");let c=i.getOption(s),u=i.getItem(s);if(t.$order=t.$order||l.$order,delete i.options[s],i.uncacheValue(a),i.options[a]=t,c){if(i.dropdown_content.contains(c)){let d=i._render("option",t);li(c,d),i.activeOption===c&&i.setActiveOption(d)}c.remove()}u&&(o=i.items.indexOf(s),o!==-1&&i.items.splice(o,1,a),r=i._render("item",t),u.classList.contains("active")&&$e(r,"active"),li(u,r)),i.isDropdownContentStale=!0}removeOption(e,t){let i=this;e=si(e),i.uncacheValue(e),delete i.userOptions[e],delete i.options[e],i.isDropdownContentStale=!0,i.trigger("option_remove",e),i.removeItem(e,t)}clearOptions(e){let t=(e||this.clearFilter).bind(this);this.loadedSearches={},this.userOptions={},this.clearCache();let i={};ve(this.options,(r,o)=>{t(r,o)&&(i[o]=r)}),this.options=this.sifter.items=i,this.isDropdownContentStale=!0,this.trigger("option_clear")}clearFilter(e,t){return this.items.indexOf(t)>=0}getOption(e,t=!1){let i=Fe(e);if(i===null)return null;let r=this.options[i];if(r!=null){if(r.$div)return r.$div;if(t)return this._render("option",r)}return null}getAdjacent(e,t,i="option"){var r=this,o;if(!e)return null;i=="item"?o=r.controlChildren():o=r.dropdown_content.querySelectorAll("[data-selectable]");for(let s=0;s0?o[s+1]:o[s-1];return null}getItem(e){if(typeof e=="object")return e;var t=Fe(e);return t!==null?this.control.querySelector(`[data-value="${Fo(t)}"]`):null}addItems(e,t){var i=this,r=Array.isArray(e)?e:[e];r=r.filter(s=>i.items.indexOf(s)===-1);let o=r[r.length-1];r.forEach(s=>{i.isPending=s!==o,i.addItem(s,t)})}addItem(e,t){var i=t?[]:["change","dropdown_close"];Po(this,i,()=>{var r,o;let s=this,a=s.settings.mode,l=Fe(e);if(!(l&&s.items.indexOf(l)!==-1&&(a==="single"&&s.close(),a==="single"||!s.settings.duplicates))&&!(l===null||!s.options.hasOwnProperty(l))&&(a==="single"&&s.clear(t),!(a==="multi"&&s.isFull()))){if(r=s._render("item",s.options[l]),s.control.contains(r)&&(r=r.cloneNode(!0)),o=s.isFull(),s.items.splice(s.caretPos,0,l),s.insertAtCaret(r),s.isSetup){if(!s.isPending&&s.settings.hideSelected){let c=s.getOption(l),u=s.getAdjacent(c,1);u&&s.setActiveOption(u)}s.settings.clearAfterSelect&&s.setTextboxValue(),!s.isPending&&!s.settings.closeAfterSelect&&s.refreshOptions(s.isFocused&&a!=="single"),s.settings.closeAfterSelect!=!1&&s.isFull()?s.close():s.isPending||s.positionDropdown(),s.trigger("item_add",l,r),s.isPending||s.updateOriginalInput({silent:t})}(!s.isPending||!o&&s.isFull())&&(s.inputState(),s.refreshState())}})}removeItem(e=null,t){let i=this;if(e=i.getItem(e),!e)return;var r,o;let s=e.dataset.value;r=Vo(e),e.remove(),e.classList.contains("active")&&(o=i.activeItems.indexOf(e),i.activeItems.splice(o,1),gt(e,"active")),i.items.splice(r,1),i.isDropdownContentStale=!0,!i.settings.persist&&i.userOptions.hasOwnProperty(s)&&i.removeOption(s,t),r{}){arguments.length===3&&(t=arguments[2]),typeof t!="function"&&(t=()=>{});var i=this,r=i.caretPos,o;if(e=e||i.inputValue(),!i.canCreate(e))return Fe(e)&&this.options[e]&&i.addItem(e),t(),!1;i.lock();var s=!1,a=l=>{if(i.unlock(),!l||typeof l!="object")return t();var c=Fe(l[i.settings.valueField]);if(typeof c!="string")return t();i.setTextboxValue(),i.addOption(l,!0),i.setCaret(r),i.addItem(c),t(l),s=!0};return typeof i.settings.create=="function"?o=i.settings.create.call(this,e,a):o={[i.settings.labelField]:e,[i.settings.valueField]:e},s||a(o),!0}refreshItems(){var e=this;e.isDropdownContentStale=!0,e.isSetup&&e.addItems(e.items),e.updateOriginalInput(),e.refreshState()}refreshState(){let e=this;e.refreshValidityState();let t=e.isFull(),i=e.isLocked;e.wrapper.classList.toggle("rtl",e.rtl);let r=e.wrapper.classList;r.toggle("focus",e.isFocused),r.toggle("disabled",e.isDisabled),r.toggle("readonly",e.isReadOnly),r.toggle("required",e.isRequired),r.toggle("invalid",!e.isValid),r.toggle("locked",i),r.toggle("full",t),r.toggle("input-active",e.isFocused&&!e.isInputHidden),r.toggle("dropdown-active",e.isOpen),r.toggle("has-options",Qa(e.options)),r.toggle("has-items",e.items.length>0)}refreshValidityState(){var e=this;e.input.validity&&(e.isValid=e.input.validity.valid,e.isInvalid=!e.isValid)}isFull(){return this.settings.maxItems!==null&&this.items.length>=this.settings.maxItems}updateOriginalInput(e={}){let t=this;var i,r;let o=t.input.querySelector('option[value=""]');if(t.is_select_tag){let l=function(c,u,d){return c||(c=nt('")),c!=o&&t.input.append(c),s.push(c),(c!=o||a>0)&&(c.selected=!0),c},s=[],a=t.input.querySelectorAll("option:checked").length;t.input.querySelectorAll("option:checked").forEach(c=>{c.selected=!1}),t.items.length==0&&t.settings.mode=="single"?l(o,"",""):t.items.forEach(c=>{if(i=t.options[c],r=i[t.settings.labelField]||"",s.includes(i.$option)){let u=t.input.querySelector(`option[value="${Fo(c)}"]:not(:checked)`);l(u,c,r)}else i.$option=l(i.$option,c,r)})}else t.input.value=t.getValue();t.isSetup&&(e.silent||t.trigger("change",t.getValue()))}open(){var e=this;e.isLocked||e.isOpen||e.settings.mode==="multi"&&e.isFull()||(e.isOpen=!0,ae(e.focus_node,{"aria-expanded":"true"}),e.refreshState(),ai(e.dropdown,{visibility:"hidden",display:"block"}),e.positionDropdown(),ai(e.dropdown,{visibility:"visible",display:"block"}),e.focus(),e.trigger("dropdown_open",e.dropdown))}close(e=!0){var t=this,i=t.isOpen;e&&(t.setTextboxValue(),t.settings.mode==="single"&&t.items.length&&t.inputState()),t.isOpen=!1,ae(t.focus_node,{"aria-expanded":"false"}),ai(t.dropdown,{display:"none"}),t.settings.hideSelected&&t.clearActiveOption(),t.refreshState(),i&&t.trigger("dropdown_close",t.dropdown)}positionDropdown(){if(this.settings.dropdownParent==="body"){var e=this.control,t=e.getBoundingClientRect(),i=e.offsetHeight+t.top+window.scrollY,r=t.left+window.scrollX;ai(this.dropdown,{width:t.width+"px",top:i+"px",left:r+"px"})}}clear(e){var t=this;if(t.items.length){var i=t.controlChildren();ve(i,r=>{t.removeItem(r,!0)}),t.inputState(),e||t.updateOriginalInput(),t.trigger("clear")}}insertAtCaret(e){let t=this,i=t.caretPos,r=t.control;r.insertBefore(e,r.children[i]||null),t.setCaret(i+1)}deleteSelection(e){var t,i,r,o,s=this;t=e&&e.keyCode===8?-1:1,i=Ya(s.control_input);let a=[];if(s.activeItems.length)o=Bo(s.activeItems,t),r=Vo(o),t>0&&r++,ve(s.activeItems,l=>a.push(l));else if((s.isFocused||s.settings.mode==="single")&&s.items.length){let l=s.controlChildren(),c;t<0&&i.start===0&&i.length===0?c=l[s.caretPos-1]:t>0&&i.start===s.inputValue().length&&(c=l[s.caretPos]),c!==void 0&&a.push(c)}if(!s.shouldDelete(a,e))return!1;for(fe(e,!0),typeof r!="undefined"&&s.setCaret(r);a.length;)s.removeItem(a.pop());return s.inputState(),s.positionDropdown(),s.refreshOptions(!1),!0}shouldDelete(e,t){let i=e.map(r=>r.dataset.value);return!(!i.length||typeof this.settings.onDelete=="function"&&this.settings.onDelete.call(this,i,t)===!1)}advanceSelection(e,t){var i,r,o=this;o.rtl&&(e*=-1),!o.inputValue().length&&(Mt(ci,t)||Mt("shiftKey",t)?(i=o.getLastActive(e),i?i.classList.contains("active")?r=o.getAdjacent(i,e,"item"):r=i:e>0?r=o.control_input.nextElementSibling:r=o.control_input.previousElementSibling,r&&(r.classList.contains("active")&&o.removeActiveItem(i),o.setActiveItemClass(r))):o.moveCaret(e))}moveCaret(e){}getLastActive(e){let t=this.control.querySelector(".last-active");if(t)return t;var i=this.control.querySelectorAll(".active");if(i)return Bo(i,e)}setCaret(e){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.setLocked(!0)}unlock(){this.setLocked(!1)}setLocked(e=this.isReadOnly||this.isDisabled){this.isLocked=e,this.refreshState()}disable(){this.setDisabled(!0),this.close()}enable(){this.setDisabled(!1)}setDisabled(e){this.focus_node.tabIndex=e?-1:this.tabIndex,this.isDisabled=e,this.input.disabled=e,this.control_input.disabled=e,this.setLocked()}setReadOnly(e){this.isReadOnly=e,this.input.readOnly=e,this.control_input.readOnly=e,this.setLocked()}destroy(){var e=this,t=e.revertSettings;e.trigger("destroy"),e.off(),e.wrapper.remove(),e.dropdown.remove(),e.input.innerHTML=t.innerHTML,e.input.tabIndex=t.tabIndex,gt(e.input,"tomselected","ts-hidden-accessible"),e._destroy(),delete e.input.tomselect}render(e,t){var i,r;let o=this;if(typeof this.settings.render[e]!="function"||(r=o.settings.render[e].call(this,t,ar),!r))return null;if(r=nt(r),e==="option"||e==="option_create"?t[o.settings.disabledField]?ae(r,{"aria-disabled":"true"}):ae(r,{"data-selectable":""}):e==="optgroup"&&(i=t.group[o.settings.optgroupValueField],ae(r,{"data-group":i}),t.group[o.settings.disabledField]&&ae(r,{"data-disabled":""})),e==="option"||e==="item"){let s=si(t[o.settings.valueField]);ae(r,{"data-value":s}),e==="item"?($e(r,o.settings.itemClass),ae(r,{"data-ts-item":""})):($e(r,o.settings.optionClass),ae(r,{role:"option",id:t.$id}),t.$div=r,o.options[s]=t)}return r}_render(e,t){let i=this.render(e,t);if(i==null)throw"HTMLElement expected";return i}clearCache(){ve(this.options,e=>{e.$div&&(e.$div.remove(),delete e.$div)})}uncacheValue(e){let t=this.getOption(e);t&&t.remove()}canCreate(e){return this.settings.create&&e.length>0&&this.settings.createFilter.call(this,e)}hook(e,t,i){var r=this,o=r[t];r[t]=function(){var s,a;return e==="after"&&(s=o.apply(r,arguments)),a=i.apply(r,arguments),e==="instead"?a:(e==="before"&&(s=o.apply(r,arguments)),s)}}};var xm=(n,e,t,i)=>{n.addEventListener(e,t,i)};function il(){xm(this.input,"change",()=>{this.sync()})}var Tm=n=>typeof n=="undefined"||n===null?null:Cm(n),Cm=n=>typeof n=="boolean"?n?"1":"0":n+"",rl=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},Sm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Am(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Am=n=>typeof n=="string"&&n.indexOf("<")>-1;function ol(n){var e=this,t=e.onOptionSelect;e.settings.hideSelected=!1;let i=Object.assign({className:"tomselect-checkbox",checkedClassNames:void 0,uncheckedClassNames:void 0},n);var r=function(a,l){l?(a.checked=!0,i.uncheckedClassNames&&a.classList.remove(...i.uncheckedClassNames),i.checkedClassNames&&a.classList.add(...i.checkedClassNames)):(a.checked=!1,i.checkedClassNames&&a.classList.remove(...i.checkedClassNames),i.uncheckedClassNames&&a.classList.add(...i.uncheckedClassNames))},o=function(a){setTimeout(()=>{var l=a.querySelector("input."+i.className);l instanceof HTMLInputElement&&r(l,a.classList.contains("selected"))},1)};e.hook("after","setupTemplates",()=>{var s=e.settings.render.option;e.settings.render.option=(a,l)=>{var c=Sm(s.call(e,a,l)),u=document.createElement("input");i.className&&u.classList.add(i.className),u.addEventListener("click",function(p){rl(p)}),u.type="checkbox";let d=Tm(a[e.settings.valueField]);return r(u,!!(d&&e.items.indexOf(d)>-1)),c.prepend(u),c}}),e.on("item_remove",s=>{var a=e.getOption(s);a&&(a.classList.remove("selected"),o(a))}),e.on("item_add",s=>{var a=e.getOption(s);a&&o(a)}),e.hook("instead","onOptionSelect",(s,a)=>{if(a.classList.contains("selected")){a.classList.remove("selected"),e.removeItem(a.dataset.value),e.refreshOptions(),rl(s,!0);return}t.call(e,s,a),o(a)})}var Dm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Om(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Om=n=>typeof n=="string"&&n.indexOf("<")>-1;function sl(n){let e=this,t=Object.assign({className:"clear-button",title:"Clear All",role:"button",tabindex:0,html:i=>`
×
`},n);e.on("initialize",()=>{var i=Dm(t.html(t));i.addEventListener("click",r=>{e.isLocked||(e.clear(),e.settings.mode==="single"&&e.settings.allowEmptyOption&&e.addItem(""),e.refreshOptions(!1),r.preventDefault(),r.stopPropagation())}),e.control.appendChild(i)})}var Lm=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},An=(n,e,t,i)=>{n.addEventListener(e,t,i)},Mm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},Nm=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(km(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},km=n=>typeof n=="string"&&n.indexOf("<")>-1,Hm=(n,e)=>{Mm(e,(t,i)=>{t==null?n.removeAttribute(i):n.setAttribute(i,""+t)})},Im=(n,e)=>{var t;(t=n.parentNode)==null||t.insertBefore(e,n.nextSibling)},Rm=(n,e)=>{var t;(t=n.parentNode)==null||t.insertBefore(e,n)},Pm=(n,e)=>{do{var t;if(e=(t=e)==null?void 0:t.previousElementSibling,n==e)return!0}while(e&&e.previousElementSibling);return!1};function al(){var n=this;if(n.settings.mode!=="multi")return;var e=n.lock,t=n.unlock;let i=!0,r;n.hook("after","setupTemplates",()=>{var o=n.settings.render.item;n.settings.render.item=(s,a)=>{let l=Nm(o.call(n,s,a));Hm(l,{draggable:"true"});let c=g=>{i||Lm(g),g.stopPropagation()},u=g=>{r=l,setTimeout(()=>{l.classList.add("ts-dragging")},0)},d=g=>{g.preventDefault(),l.classList.add("ts-drag-over"),y(l,r)},p=()=>{l.classList.remove("ts-drag-over")},y=(g,_)=>{_!==void 0&&(Pm(_,l)?Im(g,_):Rm(g,_))},m=()=>{var g;document.querySelectorAll(".ts-drag-over").forEach(C=>C.classList.remove("ts-drag-over")),(g=r)==null||g.classList.remove("ts-dragging"),r=void 0;var _=[];n.control.querySelectorAll("[data-value]").forEach(C=>{if(C.dataset.value){let O=C.dataset.value;O&&_.push(O)}}),n.setValue(_)};return An(l,"mousedown",c),An(l,"dragstart",u),An(l,"dragenter",d),An(l,"dragover",d),An(l,"dragleave",p),An(l,"dragend",m),l}}),n.hook("instead","lock",()=>(i=!1,e.call(n))),n.hook("instead","unlock",()=>(i=!0,t.call(n)))}var Fm=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},$m=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Bm(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Bm=n=>typeof n=="string"&&n.indexOf("<")>-1;function ll(n){let e=this,t=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:i=>'
'+i.title+'×
'},n);e.on("initialize",()=>{var i=$m(t.html(t)),r=i.querySelector("."+t.closeClass);r&&r.addEventListener("click",o=>{Fm(o,!0),e.close()}),e.dropdown.insertBefore(i,e.dropdown.firstChild)})}var Vm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},zm=(n,...e)=>{var t=jm(e);n=Wm(n),n.map(i=>{t.map(r=>{i.classList.remove(r)})})},jm=n=>{var e=[];return Vm(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},Wm=n=>(Array.isArray(n)||(n=[n]),n),qm=(n,e)=>{if(!n)return-1;e=e||n.nodeName;for(var t=0;n=n.previousElementSibling;)n.matches(e)&&t++;return t};function cl(){var n=this;n.hook("instead","setCaret",e=>{n.settings.mode==="single"||!n.control.contains(n.control_input)?e=n.items.length:(e=Math.max(0,Math.min(n.items.length,e)),e!=n.caretPos&&!n.isPending&&n.controlChildren().forEach((t,i)=>{i{if(!n.isFocused)return;let t=n.getLastActive(e);if(t){let i=qm(t);n.setCaret(e>0?i+1:i),n.setActiveItem(),zm(t,"last-active")}else n.setCaret(n.caretPos+e)})}var Um=(n,e=!1)=>{n&&(n.preventDefault(),e&&n.stopPropagation())},Ym=(n,e,t,i)=>{n.addEventListener(e,t,i)},Gm=(n,e)=>{if(Array.isArray(n))n.forEach(e);else for(var t in n)n.hasOwnProperty(t)&&e(n[t],t)},ul=n=>{if(n.jquery)return n[0];if(n instanceof HTMLElement)return n;if(Km(n)){var e=document.createElement("template");return e.innerHTML=n.trim(),e.content.firstChild}return document.querySelector(n)},Km=n=>typeof n=="string"&&n.indexOf("<")>-1,Xm=(n,...e)=>{var t=Qm(e);n=Jm(n),n.map(i=>{t.map(r=>{i.classList.add(r)})})},Qm=n=>{var e=[];return Gm(n,t=>{typeof t=="string"&&(t=t.trim().split(/[\t\n\f\r\s]/)),Array.isArray(t)&&(e=e.concat(t))}),e.filter(Boolean)},Jm=n=>(Array.isArray(n)||(n=[n]),n);function dl(){let n=this;n.settings.shouldOpen=!0,n.hook("before","setup",()=>{var e;n.focus_node=n.control,Xm(n.control_input,"dropdown-input");let t=ul('