Merge branch 'main' into feature
This commit is contained in:
commit
53ea48efa9
|
|
@ -15,7 +15,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.5.3
|
placeholder: v4.5.4
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox Version
|
label: NetBox Version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.5.3
|
placeholder: v4.5.4
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox Version
|
label: NetBox Version
|
||||||
description: What version of NetBox are you currently running?
|
description: What version of NetBox are you currently running?
|
||||||
placeholder: v4.5.3
|
placeholder: v4.5.4
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
name: Claude Code Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, ready_for_review, reopened]
|
||||||
|
# Optional: Only run on specific file changes
|
||||||
|
# paths:
|
||||||
|
# - "src/**/*.ts"
|
||||||
|
# - "src/**/*.tsx"
|
||||||
|
# - "src/**/*.js"
|
||||||
|
# - "src/**/*.jsx"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
claude-review:
|
||||||
|
# Optional: Filter by PR author
|
||||||
|
# if: |
|
||||||
|
# github.event.pull_request.user.login == 'external-contributor' ||
|
||||||
|
# github.event.pull_request.user.login == 'new-developer' ||
|
||||||
|
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Run Claude Code Review
|
||||||
|
id: claude-review
|
||||||
|
uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||||
|
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
||||||
|
plugins: 'code-review@claude-code-plugins'
|
||||||
|
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
||||||
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||||
|
# or https://code.claude.com/docs/en/cli-reference for available options
|
||||||
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
name: Claude Code
|
||||||
|
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
pull_request_review_comment:
|
||||||
|
types: [created]
|
||||||
|
issues:
|
||||||
|
types: [opened, assigned]
|
||||||
|
pull_request_review:
|
||||||
|
types: [submitted]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
claude:
|
||||||
|
if: |
|
||||||
|
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||||
|
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||||
|
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||||
|
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
|
id-token: write
|
||||||
|
actions: read # Required for Claude to read CI results on PRs
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Run Claude Code
|
||||||
|
id: claude
|
||||||
|
uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||||
|
|
||||||
|
# This is an optional setting that allows Claude to read CI results on PRs
|
||||||
|
additional_permissions: |
|
||||||
|
actions: read
|
||||||
|
|
||||||
|
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
||||||
|
# prompt: 'Update the pull request description to include a summary of changes.'
|
||||||
|
|
||||||
|
# Optional: Add claude_args to customize behavior and configuration
|
||||||
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||||
|
# or https://code.claude.com/docs/en/cli-reference for available options
|
||||||
|
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
||||||
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
# NetBox
|
||||||
|
|
||||||
|
Network source-of-truth and infrastructure resource modeling (IRM) tool combining DCIM and IPAM. Built on Django + PostgreSQL + Redis.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
- Python 3.12+ / Django / Django REST Framework
|
||||||
|
- PostgreSQL (required), Redis (required for caching/queuing)
|
||||||
|
- GraphQL via Strawberry, background jobs via RQ
|
||||||
|
- Docs: MkDocs (in `docs/`)
|
||||||
|
|
||||||
|
## Repository Layout
|
||||||
|
- `netbox/` — Django project root; run all `manage.py` commands from here
|
||||||
|
- `netbox/netbox/` — Core settings, URLs, WSGI entrypoint
|
||||||
|
- `netbox/<app>/` — Django apps: `circuits`, `core`, `dcim`, `ipam`, `extras`, `tenancy`, `virtualization`, `wireless`, `users`, `vpn`
|
||||||
|
- `docs/` — MkDocs documentation source
|
||||||
|
- `contrib/` — Example configs (systemd, nginx, etc.) and other resources
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
```bash
|
||||||
|
python -m venv ~/.venv/netbox
|
||||||
|
source ~/.venv/netbox/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Copy and configure
|
||||||
|
cp netbox/netbox/configuration.example.py netbox/netbox/configuration.py
|
||||||
|
# Edit configuration.py: set DATABASE, REDIS, SECRET_KEY, ALLOWED_HOSTS
|
||||||
|
|
||||||
|
cd netbox/
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py runserver
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Commands
|
||||||
|
All commands run from the `netbox/` subdirectory with venv active.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development server
|
||||||
|
python manage.py runserver
|
||||||
|
|
||||||
|
# Run full test suite
|
||||||
|
export NETBOX_CONFIGURATION=netbox.configuration_testing
|
||||||
|
python manage.py test
|
||||||
|
|
||||||
|
# Faster test runs (no DB rebuild, parallel)
|
||||||
|
python manage.py test --keepdb --parallel 4
|
||||||
|
|
||||||
|
# Migrations
|
||||||
|
python manage.py makemigrations
|
||||||
|
python manage.py migrate
|
||||||
|
|
||||||
|
# Shell
|
||||||
|
python manage.py nbshell # NetBox-enhanced shell
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Conventions
|
||||||
|
- **Apps**: Each Django app owns its models, views, API serializers, filtersets, forms, and tests.
|
||||||
|
- **REST API**: DRF serializers live in `<app>/api/serializers.py`; viewsets in `<app>/api/views.py`; URLs auto-registered in `<app>/api/urls.py`.
|
||||||
|
- **GraphQL**: Strawberry types in `<app>/graphql/types.py`.
|
||||||
|
- **Filtersets**: `<app>/filtersets.py` — used for both UI filtering and API `?filter=` params.
|
||||||
|
- **Tables**: `django-tables2` used for all object list views (`<app>/tables.py`).
|
||||||
|
- **Templates**: Django templates in `netbox/templates/<app>/`.
|
||||||
|
- **Tests**: Mirror the app structure in `<app>/tests/`. Use `netbox.configuration_testing` for test config.
|
||||||
|
|
||||||
|
## Coding Standards
|
||||||
|
- Follow existing Django conventions; don't reinvent patterns already present in the codebase.
|
||||||
|
- New models must include `created`, `last_updated` fields (inherit from `NetBoxModel` where appropriate).
|
||||||
|
- Every model exposed in the UI needs: model, serializer, filterset, form, table, views, URL route, and tests.
|
||||||
|
- API serializers must include a `url` field (absolute URL of the object).
|
||||||
|
- Use `FeatureQuery` for generic relations (config contexts, custom fields, tags, etc.).
|
||||||
|
- Avoid adding new dependencies without strong justification.
|
||||||
|
|
||||||
|
## Branch & PR Conventions
|
||||||
|
- Branch naming: `<issue-number>-short-description` (e.g., `1234-device-typerror`)
|
||||||
|
- Use the `main` branch for patch releases; `feature` tracks work for the upcoming minor/major release.
|
||||||
|
- Every PR must reference an approved GitHub issue.
|
||||||
|
- PRs must include tests for new functionality.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
- `configuration.py` is gitignored — never commit it.
|
||||||
|
- `manage.py` lives in `netbox/`, NOT the repo root. Running from the wrong directory is a common mistake.
|
||||||
|
- `NETBOX_CONFIGURATION` env var controls which settings module loads; set to `netbox.configuration_testing` for tests.
|
||||||
|
- The `extras` app is a catch-all for cross-cutting features (custom fields, tags, webhooks, scripts).
|
||||||
|
- Plugins API: only documented public APIs are stable. Internal NetBox code is subject to change without notice.
|
||||||
|
- See `docs/development/` for the full contributing guide and code style details.
|
||||||
|
|
@ -98,6 +98,10 @@ jsonschema
|
||||||
# https://python-markdown.github.io/changelog/
|
# https://python-markdown.github.io/changelog/
|
||||||
Markdown
|
Markdown
|
||||||
|
|
||||||
|
# MkDocs
|
||||||
|
# https://github.com/mkdocs/mkdocs/releases
|
||||||
|
mkdocs<2.0
|
||||||
|
|
||||||
# MkDocs Material theme (for documentation build)
|
# MkDocs Material theme (for documentation build)
|
||||||
# https://squidfunk.github.io/mkdocs-material/changelog/
|
# https://squidfunk.github.io/mkdocs-material/changelog/
|
||||||
mkdocs-material
|
mkdocs-material
|
||||||
|
|
@ -157,8 +161,7 @@ strawberry-graphql
|
||||||
|
|
||||||
# Strawberry GraphQL Django extension
|
# Strawberry GraphQL Django extension
|
||||||
# https://github.com/strawberry-graphql/strawberry-django/releases
|
# https://github.com/strawberry-graphql/strawberry-django/releases
|
||||||
# Blocked by #21450
|
strawberry-graphql-django
|
||||||
strawberry-graphql-django==0.75.0
|
|
||||||
|
|
||||||
# SVG image rendering (used for rack elevations)
|
# SVG image rendering (used for rack elevations)
|
||||||
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst
|
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst
|
||||||
|
|
|
||||||
|
|
@ -349,6 +349,7 @@
|
||||||
"5gbase-t",
|
"5gbase-t",
|
||||||
"10gbase-br-d",
|
"10gbase-br-d",
|
||||||
"10gbase-br-u",
|
"10gbase-br-u",
|
||||||
|
"10gbase-cu",
|
||||||
"10gbase-cx4",
|
"10gbase-cx4",
|
||||||
"10gbase-er",
|
"10gbase-er",
|
||||||
"10gbase-lr",
|
"10gbase-lr",
|
||||||
|
|
@ -367,6 +368,7 @@
|
||||||
"40gbase-fr4",
|
"40gbase-fr4",
|
||||||
"40gbase-lr4",
|
"40gbase-lr4",
|
||||||
"40gbase-sr4",
|
"40gbase-sr4",
|
||||||
|
"40gbase-sr4-bd",
|
||||||
"50gbase-cr",
|
"50gbase-cr",
|
||||||
"50gbase-er",
|
"50gbase-er",
|
||||||
"50gbase-fr",
|
"50gbase-fr",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,34 @@
|
||||||
# NetBox v4.5
|
# NetBox v4.5
|
||||||
|
|
||||||
|
## v4.5.4 (2026-03-03)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#21369](https://github.com/netbox-community/netbox/issues/21369) - Support lazy-loading of image attachments
|
||||||
|
* [#21385](https://github.com/netbox-community/netbox/issues/21385) - Add contact assignment support for virtual circuits
|
||||||
|
* [#21394](https://github.com/netbox-community/netbox/issues/21394) - Add 10GBASE-CU and 40GBASE-SR4 BiDi interface types
|
||||||
|
* [#21477](https://github.com/netbox-community/netbox/issues/21477) - Extend GraphQL API filters for cables
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* [#21456](https://github.com/netbox-community/netbox/issues/21456) - Improve performance of config context resolution via GraphQL API
|
||||||
|
* [#21459](https://github.com/netbox-community/netbox/issues/21459) - Avoid prefetching data for hidden table columns
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#20490](https://github.com/netbox-community/netbox/issues/20490) - Restrict visibility of scripts in list view to users with view permission
|
||||||
|
* [#20911](https://github.com/netbox-community/netbox/issues/20911) - Sort module bay options alphabetically when installing a module
|
||||||
|
* [#21347](https://github.com/netbox-community/netbox/issues/21347) - The allocation of IPv6 addresses from a non-pool prefix should start at one, not zero
|
||||||
|
* [#21429](https://github.com/netbox-community/netbox/issues/21429) - Termination type should persist when employing "create & add another" workflow for cables
|
||||||
|
* [#21478](https://github.com/netbox-community/netbox/issues/21478) - Fix GraphQL union type resolution for connected console ports
|
||||||
|
* [#21481](https://github.com/netbox-community/netbox/issues/21481) - Fix display of facility ID on rack view
|
||||||
|
* [#21518](https://github.com/netbox-community/netbox/issues/21518) - Fix decimal custom field displaying as unset when value is zero
|
||||||
|
* [#21524](https://github.com/netbox-community/netbox/issues/21524) - Avoid `IndexError` exception when encountering stale cable paths
|
||||||
|
* [#21527](https://github.com/netbox-community/netbox/issues/21527) - Fix display of primary IP address with associated NAT IP on device view
|
||||||
|
* [#21550](https://github.com/netbox-community/netbox/issues/21550) - Ensure pre-change snapshots are recorded for related objects
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v4.5.3 (2026-02-17)
|
## v4.5.3 (2026-02-17)
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, DateFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, DateFilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from circuits import models
|
from circuits import models
|
||||||
from circuits.graphql.filter_mixins import CircuitTypeFilterMixin
|
from circuits.graphql.filter_mixins import CircuitTypeFilterMixin
|
||||||
|
|
@ -62,9 +62,9 @@ class CircuitTerminationFilter(
|
||||||
upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
xconnect_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
xconnect_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
pp_info: FilterLookup[str] | None = strawberry_django.filter_field()
|
pp_info: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
# Cached relations
|
# Cached relations
|
||||||
_provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
_provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||||
|
|
@ -92,7 +92,7 @@ class CircuitFilter(
|
||||||
TenancyFilterMixin,
|
TenancyFilterMixin,
|
||||||
PrimaryModelFilter
|
PrimaryModelFilter
|
||||||
):
|
):
|
||||||
cid: FilterLookup[str] | None = strawberry_django.filter_field()
|
cid: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -145,8 +145,8 @@ class CircuitGroupAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, Cha
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Provider, lookups=True)
|
@strawberry_django.filter_type(models.Provider, lookups=True)
|
||||||
class ProviderFilter(ContactFilterMixin, PrimaryModelFilter):
|
class ProviderFilter(ContactFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | 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 = (
|
circuits: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
|
|
@ -159,18 +159,18 @@ class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
provider_id: ID | None = strawberry_django.filter_field()
|
provider_id: ID | None = strawberry_django.filter_field()
|
||||||
account: FilterLookup[str] | None = strawberry_django.filter_field()
|
account: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ProviderNetwork, lookups=True)
|
@strawberry_django.filter_type(models.ProviderNetwork, lookups=True)
|
||||||
class ProviderNetworkFilter(PrimaryModelFilter):
|
class ProviderNetworkFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
provider_id: ID | None = strawberry_django.filter_field()
|
provider_id: ID | None = strawberry_django.filter_field()
|
||||||
service_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
service_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.VirtualCircuitType, lookups=True)
|
@strawberry_django.filter_type(models.VirtualCircuitType, lookups=True)
|
||||||
|
|
@ -180,7 +180,7 @@ class VirtualCircuitTypeFilter(CircuitTypeFilterMixin, OrganizationalModelFilter
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.VirtualCircuit, lookups=True)
|
@strawberry_django.filter_type(models.VirtualCircuit, lookups=True)
|
||||||
class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilter):
|
class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
cid: FilterLookup[str] | None = strawberry_django.filter_field()
|
cid: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -218,4 +218,4 @@ class VirtualCircuitTerminationFilter(CustomFieldsFilterMixin, TagsFilterMixin,
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
interface_id: ID | None = strawberry_django.filter_field()
|
interface_id: ID | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import re
|
||||||
import typing
|
import typing
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from drf_spectacular.contrib.django_filters import DjangoFilterExtension
|
||||||
from drf_spectacular.extensions import OpenApiSerializerExtension, OpenApiSerializerFieldExtension, _SchemaType
|
from drf_spectacular.extensions import OpenApiSerializerExtension, OpenApiSerializerFieldExtension, _SchemaType
|
||||||
from drf_spectacular.openapi import AutoSchema
|
from drf_spectacular.openapi import AutoSchema
|
||||||
from drf_spectacular.plumbing import (
|
from drf_spectacular.plumbing import (
|
||||||
|
|
@ -9,6 +10,7 @@ from drf_spectacular.plumbing import (
|
||||||
build_choice_field,
|
build_choice_field,
|
||||||
build_media_type_object,
|
build_media_type_object,
|
||||||
build_object_type,
|
build_object_type,
|
||||||
|
follow_field_source,
|
||||||
get_doc,
|
get_doc,
|
||||||
)
|
)
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
|
|
@ -23,6 +25,29 @@ BULK_ACTIONS = ("bulk_destroy", "bulk_partial_update", "bulk_update")
|
||||||
WRITABLE_ACTIONS = ("PATCH", "POST", "PUT")
|
WRITABLE_ACTIONS = ("PATCH", "POST", "PUT")
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxDjangoFilterExtension(DjangoFilterExtension):
|
||||||
|
"""
|
||||||
|
Overrides drf-spectacular's DjangoFilterExtension to fix a regression in v0.29.0 where
|
||||||
|
_get_model_field() incorrectly double-appends to_field_name when field_name already ends
|
||||||
|
with that value (e.g. field_name='tags__slug', to_field_name='slug' produces the invalid
|
||||||
|
path ['tags', 'slug', 'slug']). This caused hundreds of spurious warnings during schema
|
||||||
|
generation for filters such as TagFilter, TenancyFilterSet.tenant, and OwnerFilterMixin.owner.
|
||||||
|
|
||||||
|
See: https://github.com/netbox-community/netbox/issues/20787
|
||||||
|
https://github.com/tfranzel/drf-spectacular/issues/1475
|
||||||
|
"""
|
||||||
|
priority = 1
|
||||||
|
|
||||||
|
def _get_model_field(self, filter_field, model):
|
||||||
|
if not filter_field.field_name:
|
||||||
|
return None
|
||||||
|
path = filter_field.field_name.split('__')
|
||||||
|
to_field_name = filter_field.extra.get('to_field_name')
|
||||||
|
if to_field_name is not None and path[-1] != to_field_name:
|
||||||
|
path.append(to_field_name)
|
||||||
|
return follow_field_source(model, path, emit_warnings=False)
|
||||||
|
|
||||||
|
|
||||||
class FixTimeZoneSerializerField(OpenApiSerializerFieldExtension):
|
class FixTimeZoneSerializerField(OpenApiSerializerFieldExtension):
|
||||||
target_class = 'timezone_field.rest_framework.TimeZoneSerializerField'
|
target_class = 'timezone_field.rest_framework.TimeZoneSerializerField'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from django.contrib.contenttypes.models import ContentType as DjangoContentType
|
from django.contrib.contenttypes.models import ContentType as DjangoContentType
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from core import models
|
from core import models
|
||||||
from netbox.graphql.filters import BaseModelFilter, PrimaryModelFilter
|
from netbox.graphql.filters import BaseModelFilter, PrimaryModelFilter
|
||||||
|
|
@ -32,23 +32,23 @@ class DataFileFilter(BaseModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
source_id: ID | None = strawberry_django.filter_field()
|
source_id: ID | None = strawberry_django.filter_field()
|
||||||
path: FilterLookup[str] | None = strawberry_django.filter_field()
|
path: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
hash: FilterLookup[str] | None = strawberry_django.filter_field()
|
hash: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.DataSource, lookups=True)
|
@strawberry_django.filter_type(models.DataSource, lookups=True)
|
||||||
class DataSourceFilter(PrimaryModelFilter):
|
class DataSourceFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
type: FilterLookup[str] | None = strawberry_django.filter_field()
|
type: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
source_url: FilterLookup[str] | None = strawberry_django.filter_field()
|
source_url: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: (
|
status: (
|
||||||
BaseFilterLookup[Annotated['DataSourceStatusEnum', strawberry.lazy('core.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['DataSourceStatusEnum', strawberry.lazy('core.graphql.enums')]] | None
|
||||||
) = strawberry_django.filter_field()
|
) = strawberry_django.filter_field()
|
||||||
enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
|
enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
ignore_rules: FilterLookup[str] | None = strawberry_django.filter_field()
|
ignore_rules: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -62,8 +62,8 @@ class DataSourceFilter(PrimaryModelFilter):
|
||||||
class ObjectChangeFilter(BaseModelFilter):
|
class ObjectChangeFilter(BaseModelFilter):
|
||||||
time: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
|
time: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
|
||||||
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
|
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
user_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
user_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
request_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
request_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
action: (
|
action: (
|
||||||
BaseFilterLookup[Annotated['ObjectChangeActionEnum', strawberry.lazy('core.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['ObjectChangeActionEnum', strawberry.lazy('core.graphql.enums')]] | None
|
||||||
) = strawberry_django.filter_field()
|
) = strawberry_django.filter_field()
|
||||||
|
|
@ -76,7 +76,7 @@ class ObjectChangeFilter(BaseModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
related_object_id: ID | None = strawberry_django.filter_field()
|
related_object_id: ID | None = strawberry_django.filter_field()
|
||||||
object_repr: FilterLookup[str] | None = strawberry_django.filter_field()
|
object_repr: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -87,5 +87,5 @@ class ObjectChangeFilter(BaseModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(DjangoContentType, lookups=True)
|
@strawberry_django.filter_type(DjangoContentType, lookups=True)
|
||||||
class ContentTypeFilter(BaseModelFilter):
|
class ContentTypeFilter(BaseModelFilter):
|
||||||
app_label: FilterLookup[str] | None = strawberry_django.filter_field()
|
app_label: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
model: FilterLookup[str] | None = strawberry_django.filter_field()
|
model: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@ class CablePathSerializer(serializers.ModelSerializer):
|
||||||
def get_path(self, obj):
|
def get_path(self, obj):
|
||||||
ret = []
|
ret = []
|
||||||
for nodes in obj.path_objects:
|
for nodes in obj.path_objects:
|
||||||
|
if not nodes:
|
||||||
|
# The path contains an invalid object
|
||||||
|
return []
|
||||||
serializer = get_serializer_for_model(nodes[0])
|
serializer = get_serializer_for_model(nodes[0])
|
||||||
context = {'request': self.context['request']}
|
context = {'request': self.context['request']}
|
||||||
ret.append(serializer(nodes, nested=True, many=True, context=context).data)
|
ret.append(serializer(nodes, nested=True, many=True, context=context).data)
|
||||||
|
|
|
||||||
|
|
@ -921,6 +921,7 @@ class InterfaceTypeChoices(ChoiceSet):
|
||||||
# 10 Gbps Ethernet
|
# 10 Gbps Ethernet
|
||||||
TYPE_10GE_BR_D = '10gbase-br-d'
|
TYPE_10GE_BR_D = '10gbase-br-d'
|
||||||
TYPE_10GE_BR_U = '10gbase-br-u'
|
TYPE_10GE_BR_U = '10gbase-br-u'
|
||||||
|
TYPE_10GE_CU = '10gbase-cu'
|
||||||
TYPE_10GE_CX4 = '10gbase-cx4'
|
TYPE_10GE_CX4 = '10gbase-cx4'
|
||||||
TYPE_10GE_ER = '10gbase-er'
|
TYPE_10GE_ER = '10gbase-er'
|
||||||
TYPE_10GE_LR = '10gbase-lr'
|
TYPE_10GE_LR = '10gbase-lr'
|
||||||
|
|
@ -943,6 +944,7 @@ class InterfaceTypeChoices(ChoiceSet):
|
||||||
TYPE_40GE_FR4 = '40gbase-fr4'
|
TYPE_40GE_FR4 = '40gbase-fr4'
|
||||||
TYPE_40GE_LR4 = '40gbase-lr4'
|
TYPE_40GE_LR4 = '40gbase-lr4'
|
||||||
TYPE_40GE_SR4 = '40gbase-sr4'
|
TYPE_40GE_SR4 = '40gbase-sr4'
|
||||||
|
TYPE_40GE_SR4_BD = '40gbase-sr4-bd'
|
||||||
|
|
||||||
# 50 Gbps Ethernet
|
# 50 Gbps Ethernet
|
||||||
TYPE_50GE_CR = '50gbase-cr'
|
TYPE_50GE_CR = '50gbase-cr'
|
||||||
|
|
@ -1192,6 +1194,7 @@ class InterfaceTypeChoices(ChoiceSet):
|
||||||
(
|
(
|
||||||
(TYPE_10GE_BR_D, '10GBASE-BR-D (10GE BiDi Down)'),
|
(TYPE_10GE_BR_D, '10GBASE-BR-D (10GE BiDi Down)'),
|
||||||
(TYPE_10GE_BR_U, '10GBASE-BR-U (10GE BiDi Up)'),
|
(TYPE_10GE_BR_U, '10GBASE-BR-U (10GE BiDi Up)'),
|
||||||
|
(TYPE_10GE_CU, '10GBASE-CU (10GE DAC Passive Twinax)'),
|
||||||
(TYPE_10GE_CX4, '10GBASE-CX4 (10GE DAC)'),
|
(TYPE_10GE_CX4, '10GBASE-CX4 (10GE DAC)'),
|
||||||
(TYPE_10GE_ER, '10GBASE-ER (10GE)'),
|
(TYPE_10GE_ER, '10GBASE-ER (10GE)'),
|
||||||
(TYPE_10GE_LR, '10GBASE-LR (10GE)'),
|
(TYPE_10GE_LR, '10GBASE-LR (10GE)'),
|
||||||
|
|
@ -1220,6 +1223,7 @@ class InterfaceTypeChoices(ChoiceSet):
|
||||||
(TYPE_40GE_FR4, '40GBASE-FR4 (40GE)'),
|
(TYPE_40GE_FR4, '40GBASE-FR4 (40GE)'),
|
||||||
(TYPE_40GE_LR4, '40GBASE-LR4 (40GE)'),
|
(TYPE_40GE_LR4, '40GBASE-LR4 (40GE)'),
|
||||||
(TYPE_40GE_SR4, '40GBASE-SR4 (40GE)'),
|
(TYPE_40GE_SR4, '40GBASE-SR4 (40GE)'),
|
||||||
|
(TYPE_40GE_SR4_BD, '40GBASE-SR4 (40GE BiDi)'),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -1386,6 +1386,7 @@ class MACAddressImportForm(PrimaryModelImportForm):
|
||||||
|
|
||||||
# Assign the MAC address as primary for its interface, if designated as such
|
# Assign the MAC address as primary for its interface, if designated as such
|
||||||
if interface and self.cleaned_data['is_primary'] and self.instance.pk:
|
if interface and self.cleaned_data['is_primary'] and self.instance.pk:
|
||||||
|
interface.snapshot()
|
||||||
interface.primary_mac_address = self.instance
|
interface.primary_mac_address = self.instance
|
||||||
interface.save()
|
interface.save()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ def get_cable_form(a_type, b_type):
|
||||||
|
|
||||||
def __new__(mcs, name, bases, attrs):
|
def __new__(mcs, name, bases, attrs):
|
||||||
|
|
||||||
|
# NOTE: Cable.clone() mirrors the parent selector mapping below:
|
||||||
|
# termination_{end}_device / termination_{end}_powerpanel / termination_{end}_circuit
|
||||||
|
# This supports both the "Clone" and "Create & Add Another" workflows.
|
||||||
|
# If you change the mapping here, update Cable.clone() accordingly.
|
||||||
for cable_end, term_cls in (('a', a_type), ('b', b_type)):
|
for cable_end, term_cls in (('a', a_type), ('b', b_type)):
|
||||||
|
|
||||||
# Device component
|
# Device component
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry import ID
|
from strawberry import ID
|
||||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from core.graphql.filters import ContentTypeFilter
|
from core.graphql.filters import ContentTypeFilter
|
||||||
|
|
||||||
|
|
@ -66,9 +66,9 @@ class ComponentModelFilterMixin:
|
||||||
)
|
)
|
||||||
device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
device_id: ID | None = strawberry_django.filter_field()
|
device_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
label: FilterLookup[str] | None = strawberry_django.filter_field()
|
label: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -96,9 +96,9 @@ class ComponentTemplateFilterMixin:
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
device_type_id: ID | None = strawberry_django.filter_field()
|
device_type_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
label: FilterLookup[str] | None = strawberry_django.filter_field()
|
label: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, ComparisonFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, ComparisonFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from dcim import models
|
from dcim import models
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
|
|
@ -114,7 +114,7 @@ class CableFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
status: BaseFilterLookup[Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
label: FilterLookup[str] | None = strawberry_django.filter_field()
|
label: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -141,6 +141,20 @@ class CableTerminationFilter(ChangeLoggedModelFilter):
|
||||||
)
|
)
|
||||||
termination_id: ID | None = strawberry_django.filter_field()
|
termination_id: ID | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
# Cached relations
|
||||||
|
_device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field(
|
||||||
|
name='device'
|
||||||
|
)
|
||||||
|
_rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field(
|
||||||
|
name='rack'
|
||||||
|
)
|
||||||
|
_location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
strawberry_django.filter_field(name='location')
|
||||||
|
)
|
||||||
|
_site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field(
|
||||||
|
name='site'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ConsolePort, lookups=True)
|
@strawberry_django.filter_type(models.ConsolePort, lookups=True)
|
||||||
class ConsolePortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
class ConsolePortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||||
|
|
@ -196,9 +210,9 @@ class DeviceFilter(
|
||||||
platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
serial: FilterLookup[str] | None = strawberry_django.filter_field()
|
serial: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
|
asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | 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()
|
site_id: ID | None = strawberry_django.filter_field()
|
||||||
location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
|
@ -325,7 +339,7 @@ class InventoryItemTemplateFilter(ComponentTemplateFilterMixin, ChangeLoggedMode
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
manufacturer_id: ID | None = strawberry_django.filter_field()
|
manufacturer_id: ID | None = strawberry_django.filter_field()
|
||||||
part_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
part_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.DeviceRole, lookups=True)
|
@strawberry_django.filter_type(models.DeviceRole, lookups=True)
|
||||||
|
|
@ -342,13 +356,13 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryMod
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
manufacturer_id: ID | None = strawberry_django.filter_field()
|
manufacturer_id: ID | None = strawberry_django.filter_field()
|
||||||
model: FilterLookup[str] | None = strawberry_django.filter_field()
|
model: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
default_platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
default_platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
default_platform_id: ID | None = strawberry_django.filter_field()
|
default_platform_id: ID | None = strawberry_django.filter_field()
|
||||||
part_number: FilterLookup[str] | None = strawberry_django.filter_field()
|
part_number: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
instances: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
instances: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -465,7 +479,7 @@ class PortTemplateMappingFilter(BaseModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.MACAddress, lookups=True)
|
@strawberry_django.filter_type(models.MACAddress, lookups=True)
|
||||||
class MACAddressFilter(PrimaryModelFilter):
|
class MACAddressFilter(PrimaryModelFilter):
|
||||||
mac_address: FilterLookup[str] | None = strawberry_django.filter_field()
|
mac_address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -511,7 +525,7 @@ class InterfaceFilter(
|
||||||
duplex: BaseFilterLookup[Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
duplex: BaseFilterLookup[Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
wwn: FilterLookup[str] | None = strawberry_django.filter_field()
|
wwn: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
parent: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
parent: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -631,9 +645,9 @@ class InventoryItemFilter(ComponentModelFilterMixin, NetBoxModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
manufacturer_id: ID | None = strawberry_django.filter_field()
|
manufacturer_id: ID | None = strawberry_django.filter_field()
|
||||||
part_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
part_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
serial: FilterLookup[str] | None = strawberry_django.filter_field()
|
serial: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
|
asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
discovered: FilterLookup[bool] | None = strawberry_django.filter_field()
|
discovered: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -651,7 +665,7 @@ class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilt
|
||||||
status: BaseFilterLookup[Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
facility: FilterLookup[str] | None = strawberry_django.filter_field()
|
facility: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -680,8 +694,8 @@ class ModuleFilter(ConfigContextFilterMixin, PrimaryModelFilter):
|
||||||
status: BaseFilterLookup[Annotated['ModuleStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['ModuleStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
serial: FilterLookup[str] | None = strawberry_django.filter_field()
|
serial: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
|
asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
console_ports: Annotated['ConsolePortFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
console_ports: Annotated['ConsolePortFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -720,17 +734,17 @@ class ModuleBayFilter(ModularComponentFilterMixin, NetBoxModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
parent_id: ID | None = strawberry_django.filter_field()
|
parent_id: ID | None = strawberry_django.filter_field()
|
||||||
position: FilterLookup[str] | None = strawberry_django.filter_field()
|
position: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ModuleBayTemplate, lookups=True)
|
@strawberry_django.filter_type(models.ModuleBayTemplate, lookups=True)
|
||||||
class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||||
position: FilterLookup[str] | None = strawberry_django.filter_field()
|
position: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True)
|
@strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True)
|
||||||
class ModuleTypeProfileFilter(PrimaryModelFilter):
|
class ModuleTypeProfileFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ModuleType, lookups=True)
|
@strawberry_django.filter_type(models.ModuleType, lookups=True)
|
||||||
|
|
@ -743,8 +757,8 @@ class ModuleTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryMod
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
profile_id: ID | None = strawberry_django.filter_field()
|
profile_id: ID | None = strawberry_django.filter_field()
|
||||||
model: FilterLookup[str] | None = strawberry_django.filter_field()
|
model: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
part_number: FilterLookup[str] | None = strawberry_django.filter_field()
|
part_number: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
instances: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
instances: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -804,7 +818,7 @@ class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryM
|
||||||
power_panel_id: ID | None = strawberry_django.filter_field()
|
power_panel_id: ID | None = strawberry_django.filter_field()
|
||||||
rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | 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()
|
rack_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['PowerFeedStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['PowerFeedStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -875,7 +889,7 @@ class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryMo
|
||||||
location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.PowerPort, lookups=True)
|
@strawberry_django.filter_type(models.PowerPort, lookups=True)
|
||||||
|
|
@ -913,8 +927,8 @@ class RackTypeFilter(ImageAttachmentFilterMixin, RackFilterMixin, WeightFilterMi
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
manufacturer_id: ID | None = strawberry_django.filter_field()
|
manufacturer_id: ID | None = strawberry_django.filter_field()
|
||||||
model: FilterLookup[str] | None = strawberry_django.filter_field()
|
model: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
racks: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | 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()
|
rack_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
@ -935,8 +949,8 @@ class RackFilter(
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
rack_type_id: ID | None = strawberry_django.filter_field()
|
rack_type_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
facility_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
facility_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | 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()
|
site_id: ID | None = strawberry_django.filter_field()
|
||||||
location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
|
|
@ -950,8 +964,8 @@ class RackFilter(
|
||||||
)
|
)
|
||||||
role: Annotated['RackRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
role: Annotated['RackRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
role_id: ID | None = strawberry_django.filter_field()
|
role_id: ID | None = strawberry_django.filter_field()
|
||||||
serial: FilterLookup[str] | None = strawberry_django.filter_field()
|
serial: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
|
asset_tag: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
airflow: BaseFilterLookup[Annotated['RackAirflowEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
airflow: BaseFilterLookup[Annotated['RackAirflowEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -969,7 +983,7 @@ class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
)
|
)
|
||||||
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | 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()
|
user_id: ID | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['RackReservationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['RackReservationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -1020,8 +1034,8 @@ class RegionFilter(ContactFilterMixin, NestedGroupModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Site, lookups=True)
|
@strawberry_django.filter_type(models.Site, lookups=True)
|
||||||
class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -1035,11 +1049,11 @@ class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMi
|
||||||
group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
facility: FilterLookup[str] | None = strawberry_django.filter_field()
|
facility: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
time_zone: FilterLookup[str] | None = strawberry_django.filter_field()
|
time_zone: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
physical_address: FilterLookup[str] | None = strawberry_django.filter_field()
|
physical_address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
shipping_address: FilterLookup[str] | None = strawberry_django.filter_field()
|
shipping_address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -1068,8 +1082,8 @@ class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilter):
|
||||||
class VirtualChassisFilter(PrimaryModelFilter):
|
class VirtualChassisFilter(PrimaryModelFilter):
|
||||||
master: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
master: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
master_id: ID | None = strawberry_django.filter_field()
|
master_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
domain: FilterLookup[str] | None = strawberry_django.filter_field()
|
domain: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
members: (
|
members: (
|
||||||
Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None
|
Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None
|
||||||
) = strawberry_django.filter_field()
|
) = strawberry_django.filter_field()
|
||||||
|
|
@ -1080,7 +1094,7 @@ class VirtualChassisFilter(PrimaryModelFilter):
|
||||||
class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilter):
|
class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
device_id: ID | None = strawberry_django.filter_field()
|
device_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: (
|
status: (
|
||||||
BaseFilterLookup[Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None
|
||||||
) = (
|
) = (
|
||||||
|
|
@ -1097,7 +1111,7 @@ class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
primary_ip6_id: ID | None = strawberry_django.filter_field()
|
primary_ip6_id: ID | None = strawberry_django.filter_field()
|
||||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
comments: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
interfaces: (
|
interfaces: (
|
||||||
Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None
|
Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None
|
||||||
) = strawberry_django.filter_field()
|
) = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,50 @@ class Cable(PrimaryModel):
|
||||||
except UnsupportedCablePath as e:
|
except UnsupportedCablePath as e:
|
||||||
raise AbortRequest(e)
|
raise AbortRequest(e)
|
||||||
|
|
||||||
|
def clone(self):
|
||||||
|
"""
|
||||||
|
Return attributes suitable for cloning this cable.
|
||||||
|
|
||||||
|
In addition to the fields defined in `clone_fields`, include the termination
|
||||||
|
type and parent selector fields used by dcim.forms.connections.get_cable_form().
|
||||||
|
"""
|
||||||
|
attrs = super().clone()
|
||||||
|
|
||||||
|
# Mirror dcim.forms.connections.get_cable_form() parent-field logic
|
||||||
|
for cable_end, terminations in (('a', self.a_terminations), ('b', self.b_terminations)):
|
||||||
|
if not terminations:
|
||||||
|
continue
|
||||||
|
|
||||||
|
term_cls = type(terminations[0])
|
||||||
|
term_label = term_cls._meta.label_lower
|
||||||
|
|
||||||
|
# Matches CableForm choices: "<app_label>.<model>"
|
||||||
|
attrs[f'{cable_end}_terminations_type'] = term_label
|
||||||
|
|
||||||
|
# Device component
|
||||||
|
if hasattr(term_cls, 'device'):
|
||||||
|
device_ids = sorted({t.device_id for t in terminations if t.device_id})
|
||||||
|
if device_ids:
|
||||||
|
attrs[f'termination_{cable_end}_device'] = device_ids
|
||||||
|
|
||||||
|
# PowerFeed
|
||||||
|
elif term_label == 'dcim.powerfeed':
|
||||||
|
powerpanel_ids = sorted({t.power_panel_id for t in terminations if t.power_panel_id})
|
||||||
|
if powerpanel_ids:
|
||||||
|
attrs[f'termination_{cable_end}_powerpanel'] = powerpanel_ids
|
||||||
|
|
||||||
|
# CircuitTermination
|
||||||
|
elif term_label == 'circuits.circuittermination':
|
||||||
|
circuit_ids = sorted({t.circuit_id for t in terminations if t.circuit_id})
|
||||||
|
if circuit_ids:
|
||||||
|
attrs[f'termination_{cable_end}_circuit'] = circuit_ids
|
||||||
|
|
||||||
|
# Never clone the actual terminations, as they are already occupied
|
||||||
|
attrs.pop('a_terminations', None)
|
||||||
|
attrs.pop('b_terminations', None)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
def serialize_object(self, exclude=None):
|
def serialize_object(self, exclude=None):
|
||||||
data = serialize_object(self, exclude=exclude or [])
|
data = serialize_object(self, exclude=exclude or [])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2614,6 +2614,126 @@ class CableTest(APIViewTestCases.APIViewTestCase):
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def test_graphql_cable_termination_cached_filters(self):
|
||||||
|
"""
|
||||||
|
Validate filtering cables by cached CableTermination relations via GraphQL:
|
||||||
|
|
||||||
|
cable_list(filters: { terminations: { <relation>: {...}, DISTINCT: true } })
|
||||||
|
|
||||||
|
Also asserts deduplication when both ends match (cable between two interfaces
|
||||||
|
on the same device/rack/location/site).
|
||||||
|
"""
|
||||||
|
self.add_permissions(
|
||||||
|
'dcim.view_cable',
|
||||||
|
'dcim.view_device',
|
||||||
|
'dcim.view_interface',
|
||||||
|
'dcim.view_rack',
|
||||||
|
'dcim.view_location',
|
||||||
|
'dcim.view_site',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Reuse existing fixtures from setUpTestData()
|
||||||
|
devicetype = DeviceType.objects.get(slug='device-type-1')
|
||||||
|
role = DeviceRole.objects.get(slug='device-role-1')
|
||||||
|
|
||||||
|
# Create an isolated topology for this test
|
||||||
|
site_a = Site.objects.create(name='GQL Site A', slug='gql-site-a')
|
||||||
|
site_b = Site.objects.create(name='GQL Site B', slug='gql-site-b')
|
||||||
|
|
||||||
|
location_a = Location.objects.create(
|
||||||
|
site=site_a,
|
||||||
|
name='GQL Location A',
|
||||||
|
slug='gql-location-a',
|
||||||
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
|
)
|
||||||
|
location_b = Location.objects.create(
|
||||||
|
site=site_b,
|
||||||
|
name='GQL Location B',
|
||||||
|
slug='gql-location-b',
|
||||||
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
|
)
|
||||||
|
|
||||||
|
rack_a = Rack.objects.create(site=site_a, location=location_a, name='GQL Rack A', u_height=42)
|
||||||
|
rack_b = Rack.objects.create(site=site_b, location=location_b, name='GQL Rack B', u_height=42)
|
||||||
|
|
||||||
|
device_a = Device.objects.create(
|
||||||
|
device_type=devicetype,
|
||||||
|
role=role,
|
||||||
|
name='GQL Device A',
|
||||||
|
site=site_a,
|
||||||
|
location=location_a,
|
||||||
|
rack=rack_a,
|
||||||
|
)
|
||||||
|
device_b = Device.objects.create(
|
||||||
|
device_type=devicetype,
|
||||||
|
role=role,
|
||||||
|
name='GQL Device B',
|
||||||
|
site=site_b,
|
||||||
|
location=location_b,
|
||||||
|
rack=rack_b,
|
||||||
|
)
|
||||||
|
|
||||||
|
a0 = Interface.objects.create(device=device_a, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name='eth0')
|
||||||
|
a1 = Interface.objects.create(device=device_a, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name='eth1')
|
||||||
|
a2 = Interface.objects.create(device=device_a, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name='eth2')
|
||||||
|
b0 = Interface.objects.create(device=device_b, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name='eth0')
|
||||||
|
|
||||||
|
# Both ends on Device A (duplication risk without DISTINCT)
|
||||||
|
cable_same_device = Cable(a_terminations=[a0], b_terminations=[a1], label='GQL Cable Same Device')
|
||||||
|
cable_same_device.save()
|
||||||
|
|
||||||
|
# Cross to Device B
|
||||||
|
cable_cross = Cable(a_terminations=[a2], b_terminations=[b0], label='GQL Cable Cross')
|
||||||
|
cable_cross.save()
|
||||||
|
|
||||||
|
expected_a = {str(cable_same_device.pk), str(cable_cross.pk)}
|
||||||
|
expected_b = {str(cable_cross.pk)}
|
||||||
|
|
||||||
|
url = reverse('graphql')
|
||||||
|
|
||||||
|
test_cases = (
|
||||||
|
# Device (ID + name)
|
||||||
|
(f'device: {{ id: {{ exact: "{device_a.pk}" }} }}', expected_a),
|
||||||
|
(f'device: {{ name: {{ exact: "{device_a.name}" }} }}', expected_a),
|
||||||
|
(f'device: {{ id: {{ exact: "{device_b.pk}" }} }}', expected_b),
|
||||||
|
(f'device: {{ name: {{ exact: "{device_b.name}" }} }}', expected_b),
|
||||||
|
# Rack (ID + name)
|
||||||
|
(f'rack: {{ id: {{ exact: "{rack_a.pk}" }} }}', expected_a),
|
||||||
|
(f'rack: {{ name: {{ exact: "{rack_a.name}" }} }}', expected_a),
|
||||||
|
(f'rack: {{ id: {{ exact: "{rack_b.pk}" }} }}', expected_b),
|
||||||
|
(f'rack: {{ name: {{ exact: "{rack_b.name}" }} }}', expected_b),
|
||||||
|
# Location (ID + name)
|
||||||
|
(f'location: {{ id: {{ exact: "{location_a.pk}" }} }}', expected_a),
|
||||||
|
(f'location: {{ name: {{ exact: "{location_a.name}" }} }}', expected_a),
|
||||||
|
(f'location: {{ id: {{ exact: "{location_b.pk}" }} }}', expected_b),
|
||||||
|
(f'location: {{ name: {{ exact: "{location_b.name}" }} }}', expected_b),
|
||||||
|
# Site (ID + slug)
|
||||||
|
(f'site: {{ id: {{ exact: "{site_a.pk}" }} }}', expected_a),
|
||||||
|
(f'site: {{ slug: {{ exact: "{site_a.slug}" }} }}', expected_a),
|
||||||
|
(f'site: {{ id: {{ exact: "{site_b.pk}" }} }}', expected_b),
|
||||||
|
(f'site: {{ slug: {{ exact: "{site_b.slug}" }} }}', expected_b),
|
||||||
|
)
|
||||||
|
|
||||||
|
for inner_filter, expected in test_cases:
|
||||||
|
with self.subTest(filter=inner_filter):
|
||||||
|
query = f"""{{
|
||||||
|
cable_list(filters: {{ terminations: {{ {inner_filter} DISTINCT: true }} }})
|
||||||
|
{{ id }}
|
||||||
|
}}"""
|
||||||
|
|
||||||
|
response = self.client.post(url, data={'query': query}, format='json', **self.header)
|
||||||
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
|
data = response.json()
|
||||||
|
self.assertNotIn('errors', data)
|
||||||
|
|
||||||
|
rows = data['data']['cable_list']
|
||||||
|
ids = [row['id'] for row in rows]
|
||||||
|
|
||||||
|
# Ensure DISTINCT is actually effective (no duplicate cables when both ends match)
|
||||||
|
self.assertEqual(len(ids), len(set(ids)), f'Duplicate cables returned for: {inner_filter}')
|
||||||
|
|
||||||
|
self.assertSetEqual(set(ids), expected)
|
||||||
|
|
||||||
|
|
||||||
class CableTerminationTest(
|
class CableTerminationTest(
|
||||||
APIViewTestCases.GetObjectViewTestCase,
|
APIViewTestCases.GetObjectViewTestCase,
|
||||||
|
|
|
||||||
|
|
@ -2733,6 +2733,7 @@ class DeviceBulkImportView(generic.BulkImportView):
|
||||||
# For child devices, save the reverse relation to the parent device bay
|
# For child devices, save the reverse relation to the parent device bay
|
||||||
if parent_bay:
|
if parent_bay:
|
||||||
device_bay = parent_bay
|
device_bay = parent_bay
|
||||||
|
device_bay.snapshot()
|
||||||
device_bay.installed_device = obj
|
device_bay.installed_device = obj
|
||||||
device_bay.save()
|
device_bay.save()
|
||||||
|
|
||||||
|
|
@ -3912,19 +3913,6 @@ class CableEditView(generic.ObjectEditView):
|
||||||
|
|
||||||
return super().alter_object(obj, request, url_args, url_kwargs)
|
return super().alter_object(obj, request, url_args, url_kwargs)
|
||||||
|
|
||||||
def get_extra_addanother_params(self, request):
|
|
||||||
|
|
||||||
params = {
|
|
||||||
'a_terminations_type': request.GET.get('a_terminations_type'),
|
|
||||||
'b_terminations_type': request.GET.get('b_terminations_type')
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in request.POST:
|
|
||||||
if 'device' in key or 'power_panel' in key or 'circuit' in key:
|
|
||||||
params.update({key: request.POST.get(key)})
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
@register_model_view(Cable, 'delete')
|
@register_model_view(Cable, 'delete')
|
||||||
class CableDeleteView(generic.ObjectDeleteView):
|
class CableDeleteView(generic.ObjectDeleteView):
|
||||||
|
|
@ -4099,6 +4087,7 @@ class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, V
|
||||||
members = formset.save(commit=False)
|
members = formset.save(commit=False)
|
||||||
devices = Device.objects.filter(pk__in=[m.pk for m in members])
|
devices = Device.objects.filter(pk__in=[m.pk for m in members])
|
||||||
for device in devices:
|
for device in devices:
|
||||||
|
device.snapshot()
|
||||||
device.vc_position = None
|
device.vc_position = None
|
||||||
device.save()
|
device.save()
|
||||||
for member in members:
|
for member in members:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from extras import models
|
from extras import models
|
||||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
||||||
|
|
@ -50,11 +50,11 @@ __all__ = (
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ConfigContext, lookups=True)
|
@strawberry_django.filter_type(models.ConfigContext, lookups=True)
|
||||||
class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
|
is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
|
|
@ -107,22 +107,22 @@ class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ConfigContextProfile, lookups=True)
|
@strawberry_django.filter_type(models.ConfigContextProfile, lookups=True)
|
||||||
class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilter):
|
class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] = strawberry_django.filter_field()
|
name: StrFilterLookup[str] = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] = strawberry_django.filter_field()
|
description: StrFilterLookup[str] = strawberry_django.filter_field()
|
||||||
tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | 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)
|
@strawberry_django.filter_type(models.ConfigTemplate, lookups=True)
|
||||||
class ConfigTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
class ConfigTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
template_code: FilterLookup[str] | None = strawberry_django.filter_field()
|
template_code: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
mime_type: FilterLookup[str] | None = strawberry_django.filter_field()
|
mime_type: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
file_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
file_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
file_extension: FilterLookup[str] | None = strawberry_django.filter_field()
|
file_extension: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()
|
as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -137,10 +137,10 @@ class CustomFieldFilter(ChangeLoggedModelFilter):
|
||||||
related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
label: FilterLookup[str] | None = strawberry_django.filter_field()
|
label: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
group_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
group_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
required: FilterLookup[bool] | None = strawberry_django.filter_field()
|
required: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
unique: 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 = (
|
search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
|
|
@ -166,7 +166,7 @@ class CustomFieldFilter(ChangeLoggedModelFilter):
|
||||||
validation_maximum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
validation_maximum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
validation_regex: FilterLookup[str] | None = strawberry_django.filter_field()
|
validation_regex: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -182,13 +182,13 @@ class CustomFieldFilter(ChangeLoggedModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field()
|
is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
comments: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.CustomFieldChoiceSet, lookups=True)
|
@strawberry_django.filter_type(models.CustomFieldChoiceSet, lookups=True)
|
||||||
class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter):
|
class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
base_choices: (
|
base_choices: (
|
||||||
BaseFilterLookup[Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')]] | None
|
||||||
) = (
|
) = (
|
||||||
|
|
@ -202,14 +202,14 @@ class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.CustomLink, lookups=True)
|
@strawberry_django.filter_type(models.CustomLink, lookups=True)
|
||||||
class CustomLinkFilter(ChangeLoggedModelFilter):
|
class CustomLinkFilter(ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
|
enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
link_text: FilterLookup[str] | None = strawberry_django.filter_field()
|
link_text: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
link_url: FilterLookup[str] | None = strawberry_django.filter_field()
|
link_url: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
group_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
group_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
button_class: (
|
button_class: (
|
||||||
BaseFilterLookup[Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')]] | None
|
||||||
) = (
|
) = (
|
||||||
|
|
@ -220,15 +220,15 @@ class CustomLinkFilter(ChangeLoggedModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ExportTemplate, lookups=True)
|
@strawberry_django.filter_type(models.ExportTemplate, lookups=True)
|
||||||
class ExportTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
class ExportTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
template_code: FilterLookup[str] | None = strawberry_django.filter_field()
|
template_code: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
mime_type: FilterLookup[str] | None = strawberry_django.filter_field()
|
mime_type: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
file_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
file_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
file_extension: FilterLookup[str] | None = strawberry_django.filter_field()
|
file_extension: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()
|
as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -244,7 +244,7 @@ class ImageAttachmentFilter(ChangeLoggedModelFilter):
|
||||||
image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.JournalEntry, lookups=True)
|
@strawberry_django.filter_type(models.JournalEntry, lookups=True)
|
||||||
|
|
@ -260,22 +260,22 @@ class JournalEntryFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedM
|
||||||
kind: BaseFilterLookup[Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
|
kind: BaseFilterLookup[Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
comments: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.NotificationGroup, lookups=True)
|
@strawberry_django.filter_type(models.NotificationGroup, lookups=True)
|
||||||
class NotificationGroupFilter(ChangeLoggedModelFilter):
|
class NotificationGroupFilter(ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | 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()
|
users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.SavedFilter, lookups=True)
|
@strawberry_django.filter_type(models.SavedFilter, lookups=True)
|
||||||
class SavedFilterFilter(ChangeLoggedModelFilter):
|
class SavedFilterFilter(ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | 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()
|
user_id: ID | None = strawberry_django.filter_field()
|
||||||
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
|
|
@ -290,8 +290,8 @@ class SavedFilterFilter(ChangeLoggedModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.TableConfig, lookups=True)
|
@strawberry_django.filter_type(models.TableConfig, lookups=True)
|
||||||
class TableConfigFilter(ChangeLoggedModelFilter):
|
class TableConfigFilter(ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | 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()
|
user_id: ID | None = strawberry_django.filter_field()
|
||||||
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
|
|
@ -303,30 +303,30 @@ class TableConfigFilter(ChangeLoggedModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Tag, lookups=True)
|
@strawberry_django.filter_type(models.Tag, lookups=True)
|
||||||
class TagFilter(ChangeLoggedModelFilter):
|
class TagFilter(ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Webhook, lookups=True)
|
@strawberry_django.filter_type(models.Webhook, lookups=True)
|
||||||
class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
payload_url: FilterLookup[str] | None = strawberry_django.filter_field()
|
payload_url: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
http_method: (
|
http_method: (
|
||||||
BaseFilterLookup[Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')]] | None
|
||||||
) = (
|
) = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
http_content_type: FilterLookup[str] | None = strawberry_django.filter_field()
|
http_content_type: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
additional_headers: FilterLookup[str] | None = strawberry_django.filter_field()
|
additional_headers: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
body_template: FilterLookup[str] | None = strawberry_django.filter_field()
|
body_template: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
secret: FilterLookup[str] | None = strawberry_django.filter_field()
|
secret: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field()
|
ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
ca_file_path: FilterLookup[str] | None = strawberry_django.filter_field()
|
ca_file_path: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -334,8 +334,8 @@ class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelF
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.EventRule, lookups=True)
|
@strawberry_django.filter_type(models.EventRule, lookups=True)
|
||||||
class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -346,10 +346,10 @@ class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedMode
|
||||||
action_type: BaseFilterLookup[Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
|
action_type: BaseFilterLookup[Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
action_object_type: FilterLookup[str] | None = strawberry_django.filter_field()
|
action_object_type: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
action_object_type_id: ID | None = strawberry_django.filter_field()
|
action_object_type_id: ID | None = strawberry_django.filter_field()
|
||||||
action_object_id: ID | None = strawberry_django.filter_field()
|
action_object_id: ID | None = strawberry_django.filter_field()
|
||||||
action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
comments: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,19 @@ if TYPE_CHECKING:
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class ConfigContextMixin:
|
class ConfigContextMixin:
|
||||||
|
|
||||||
@strawberry_django.field
|
@classmethod
|
||||||
|
def get_queryset(cls, queryset, info: Info, **kwargs):
|
||||||
|
queryset = super().get_queryset(queryset, info, **kwargs)
|
||||||
|
|
||||||
|
# If `config_context` is requested, call annotate_config_context_data() on the queryset
|
||||||
|
selected = {f.name for f in info.selected_fields[0].selections}
|
||||||
|
if 'config_context' in selected and hasattr(queryset, 'annotate_config_context_data'):
|
||||||
|
return queryset.annotate_config_context_data()
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
# Ensure `local_context_data` is fetched when `config_context` is requested
|
||||||
|
@strawberry_django.field(only=['local_context_data'])
|
||||||
def config_context(self) -> strawberry.scalars.JSON:
|
def config_context(self) -> strawberry.scalars.JSON:
|
||||||
return self.get_config_context()
|
return self.get_config_context()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,7 @@ class IPAddressImportForm(PrimaryModelImportForm):
|
||||||
# Set as primary for device/VM
|
# Set as primary for device/VM
|
||||||
if self.cleaned_data.get('is_primary') is not None:
|
if self.cleaned_data.get('is_primary') is not None:
|
||||||
parent = self.cleaned_data.get('device') or self.cleaned_data.get('virtual_machine')
|
parent = self.cleaned_data.get('device') or self.cleaned_data.get('virtual_machine')
|
||||||
|
parent.snapshot()
|
||||||
if self.instance.address.version == 4:
|
if self.instance.address.version == 4:
|
||||||
parent.primary_ip4 = ipaddress if self.cleaned_data.get('is_primary') else None
|
parent.primary_ip4 = ipaddress if self.cleaned_data.get('is_primary') else None
|
||||||
elif self.instance.address.version == 6:
|
elif self.instance.address.version == 6:
|
||||||
|
|
@ -433,6 +434,7 @@ class IPAddressImportForm(PrimaryModelImportForm):
|
||||||
# Set as OOB for device
|
# Set as OOB for device
|
||||||
if self.cleaned_data.get('is_oob') is not None:
|
if self.cleaned_data.get('is_oob') is not None:
|
||||||
parent = self.cleaned_data.get('device')
|
parent = self.cleaned_data.get('device')
|
||||||
|
parent.snapshot()
|
||||||
parent.oob_ip = ipaddress if self.cleaned_data.get('is_oob') else None
|
parent.oob_ip = ipaddress if self.cleaned_data.get('is_oob') else None
|
||||||
parent.save()
|
parent.save()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import strawberry_django
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from netaddr.core import AddrFormatError
|
from netaddr.core import AddrFormatError
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, DateFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, DateFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from dcim.graphql.filter_mixins import ScopedFilterMixin
|
from dcim.graphql.filter_mixins import ScopedFilterMixin
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
|
|
@ -70,8 +70,8 @@ class ASNFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ASNRange, lookups=True)
|
@strawberry_django.filter_type(models.ASNRange, lookups=True)
|
||||||
class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter):
|
class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | 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()
|
rir_id: ID | None = strawberry_django.filter_field()
|
||||||
start: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
start: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
|
|
@ -84,7 +84,7 @@ class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Aggregate, lookups=True)
|
@strawberry_django.filter_type(models.Aggregate, lookups=True)
|
||||||
class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
prefix: FilterLookup[str] | None = strawberry_django.filter_field()
|
prefix: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | 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()
|
rir_id: ID | None = strawberry_django.filter_field()
|
||||||
date_added: DateFilterLookup[date] | None = strawberry_django.filter_field()
|
date_added: DateFilterLookup[date] | None = strawberry_django.filter_field()
|
||||||
|
|
@ -120,14 +120,14 @@ class FHRPGroupFilter(PrimaryModelFilter):
|
||||||
group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
protocol: BaseFilterLookup[Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
protocol: BaseFilterLookup[Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
auth_type: BaseFilterLookup[Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
auth_type: BaseFilterLookup[Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
auth_key: FilterLookup[str] | None = strawberry_django.filter_field()
|
auth_key: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -138,7 +138,7 @@ class FHRPGroupAssignmentFilter(ChangeLoggedModelFilter):
|
||||||
interface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
interface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
interface_id: FilterLookup[str] | None = strawberry_django.filter_field()
|
interface_id: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
group: Annotated['FHRPGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
group: Annotated['FHRPGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -174,7 +174,7 @@ class FHRPGroupAssignmentFilter(ChangeLoggedModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IPAddress, lookups=True)
|
@strawberry_django.filter_type(models.IPAddress, lookups=True)
|
||||||
class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
address: FilterLookup[str] | None = strawberry_django.filter_field()
|
address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | 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()
|
vrf_id: ID | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
||||||
|
|
@ -195,7 +195,7 @@ class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
nat_outside_id: ID | None = strawberry_django.filter_field()
|
nat_outside_id: ID | None = strawberry_django.filter_field()
|
||||||
dns_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
dns_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
@strawberry_django.filter_field()
|
@strawberry_django.filter_field()
|
||||||
def assigned(self, value: bool, prefix) -> Q:
|
def assigned(self, value: bool, prefix) -> Q:
|
||||||
|
|
@ -225,8 +225,8 @@ class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IPRange, lookups=True)
|
@strawberry_django.filter_type(models.IPRange, lookups=True)
|
||||||
class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
start_address: FilterLookup[str] | None = strawberry_django.filter_field()
|
start_address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
end_address: FilterLookup[str] | None = strawberry_django.filter_field()
|
end_address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -279,7 +279,7 @@ class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Prefix, lookups=True)
|
@strawberry_django.filter_type(models.Prefix, lookups=True)
|
||||||
class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
prefix: FilterLookup[str] | None = strawberry_django.filter_field()
|
prefix: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | 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()
|
vrf_id: ID | None = strawberry_django.filter_field()
|
||||||
vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
||||||
|
|
@ -328,7 +328,7 @@ class RoleFilter(OrganizationalModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.RouteTarget, lookups=True)
|
@strawberry_django.filter_type(models.RouteTarget, lookups=True)
|
||||||
class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilter):
|
class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
importing_vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
importing_vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -345,7 +345,7 @@ class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Service, lookups=True)
|
@strawberry_django.filter_type(models.Service, lookups=True)
|
||||||
class ServiceFilter(ContactFilterMixin, ServiceFilterMixin, PrimaryModelFilter):
|
class ServiceFilter(ContactFilterMixin, ServiceFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -357,7 +357,7 @@ class ServiceFilter(ContactFilterMixin, ServiceFilterMixin, PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.ServiceTemplate, lookups=True)
|
@strawberry_django.filter_type(models.ServiceTemplate, lookups=True)
|
||||||
class ServiceTemplateFilter(ServiceFilterMixin, PrimaryModelFilter):
|
class ServiceTemplateFilter(ServiceFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.VLAN, lookups=True)
|
@strawberry_django.filter_type(models.VLAN, lookups=True)
|
||||||
|
|
@ -371,7 +371,7 @@ class VLANFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -401,7 +401,7 @@ class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.VLANTranslationPolicy, lookups=True)
|
@strawberry_django.filter_type(models.VLANTranslationPolicy, lookups=True)
|
||||||
class VLANTranslationPolicyFilter(PrimaryModelFilter):
|
class VLANTranslationPolicyFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.VLANTranslationRule, lookups=True)
|
@strawberry_django.filter_type(models.VLANTranslationRule, lookups=True)
|
||||||
|
|
@ -410,7 +410,7 @@ class VLANTranslationRuleFilter(NetBoxModelFilter):
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
policy_id: ID | None = strawberry_django.filter_field()
|
policy_id: ID | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
local_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
local_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -421,8 +421,8 @@ class VLANTranslationRuleFilter(NetBoxModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.VRF, lookups=True)
|
@strawberry_django.filter_type(models.VRF, lookups=True)
|
||||||
class VRFFilter(TenancyFilterMixin, PrimaryModelFilter):
|
class VRFFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
rd: FilterLookup[str] | None = strawberry_django.filter_field()
|
rd: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field()
|
enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -432,9 +432,11 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary
|
||||||
])
|
])
|
||||||
available_ips = prefix - child_ips - child_ranges
|
available_ips = prefix - child_ips - child_ranges
|
||||||
|
|
||||||
# IPv6 /127's, pool, or IPv4 /31-/32 sets are fully usable
|
# Pool, IPv4 /31-/32 or IPv6 /127-/128 sets are fully usable
|
||||||
if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or (
|
if (
|
||||||
self.family == 4 and self.prefix.prefixlen >= 31
|
self.is_pool
|
||||||
|
or (self.family == 4 and self.prefix.prefixlen >= 31)
|
||||||
|
or (self.family == 6 and self.prefix.prefixlen >= 127)
|
||||||
):
|
):
|
||||||
return available_ips
|
return available_ips
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,3 +39,132 @@ class AnnotatedIPAddressTableTest(TestCase):
|
||||||
|
|
||||||
iprange_checkbox_count = html.count(f'name="pk" value="{self.ip_range.pk}"')
|
iprange_checkbox_count = html.count(f'name="pk" value="{self.ip_range.pk}"')
|
||||||
self.assertEqual(iprange_checkbox_count, 0)
|
self.assertEqual(iprange_checkbox_count, 0)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv4_non_pool_excludes_network_and_broadcast(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('192.0.2.0/29'), # 8 addresses total
|
||||||
|
status='active',
|
||||||
|
is_pool=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# /29 non-pool: exclude .0 (network) and .7 (broadcast)
|
||||||
|
self.assertEqual(available.first_ip, '192.0.2.1/29')
|
||||||
|
self.assertEqual(available.size, 6)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv4_pool_includes_network_and_broadcast(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('192.0.2.8/29'), # 8 addresses total
|
||||||
|
status='active',
|
||||||
|
is_pool=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# Pool: all addresses are usable, including network/broadcast
|
||||||
|
self.assertEqual(available.first_ip, '192.0.2.8/29')
|
||||||
|
self.assertEqual(available.size, 8)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv4_31_includes_all_ips(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('192.0.2.16/31'), # 2 addresses total
|
||||||
|
status='active',
|
||||||
|
is_pool=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# /31: fully usable
|
||||||
|
self.assertEqual(available.first_ip, '192.0.2.16/31')
|
||||||
|
self.assertEqual(available.size, 2)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv4_32_includes_single_ip(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('192.0.2.100/32'), # 1 address total
|
||||||
|
status='active',
|
||||||
|
is_pool=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# /32: single usable address
|
||||||
|
self.assertEqual(available.first_ip, '192.0.2.100/32')
|
||||||
|
self.assertEqual(available.size, 1)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv6_non_pool_excludes_anycast_first_ip(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('2001:db8::/126'), # 4 addresses total
|
||||||
|
status='active',
|
||||||
|
is_pool=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
# No child records -> expect one AvailableIPSpace entry
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# For IPv6 non-pool prefixes (except /127-/128), the first address is reserved (subnet-router anycast)
|
||||||
|
self.assertEqual(available.first_ip, '2001:db8::1/126')
|
||||||
|
self.assertEqual(available.size, 3) # 4 total - 1 reserved anycast
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv6_127_includes_all_ips(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('2001:db8::/127'), # 2 addresses total
|
||||||
|
status='active',
|
||||||
|
is_pool=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# /127 is fully usable (no anycast exclusion)
|
||||||
|
self.assertEqual(available.first_ip, '2001:db8::/127')
|
||||||
|
self.assertEqual(available.size, 2)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv6_128_includes_single_ip(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('2001:db8::1/128'), # 1 address total
|
||||||
|
status='active',
|
||||||
|
is_pool=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# /128 is fully usable (single host address)
|
||||||
|
self.assertEqual(available.first_ip, '2001:db8::1/128')
|
||||||
|
self.assertEqual(available.size, 1)
|
||||||
|
|
||||||
|
def test_annotate_ip_space_ipv6_pool_includes_anycast_first_ip(self):
|
||||||
|
prefix = Prefix.objects.create(
|
||||||
|
prefix=IPNetwork('2001:db8:1::/126'), # 4 addresses total
|
||||||
|
status='active',
|
||||||
|
is_pool=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = annotate_ip_space(prefix)
|
||||||
|
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
available = data[0]
|
||||||
|
|
||||||
|
# Pools are fully usable
|
||||||
|
self.assertEqual(available.first_ip, '2001:db8:1::/126')
|
||||||
|
self.assertEqual(available.size, 4)
|
||||||
|
|
|
||||||
|
|
@ -78,12 +78,21 @@ def annotate_ip_space(prefix):
|
||||||
records = sorted(records, key=lambda x: x[0])
|
records = sorted(records, key=lambda x: x[0])
|
||||||
|
|
||||||
# Determine the first & last valid IP addresses in the prefix
|
# Determine the first & last valid IP addresses in the prefix
|
||||||
if prefix.family == 4 and prefix.mask_length < 31 and not prefix.is_pool:
|
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
|
# Ignore the network and broadcast addresses for non-pool IPv4 prefixes larger than /31
|
||||||
first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first + 1)
|
first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first + 1)
|
||||||
last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last - 1)
|
last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last - 1)
|
||||||
else:
|
else:
|
||||||
first_ip_in_prefix = netaddr.IPAddress(prefix.prefix.first)
|
# 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)
|
last_ip_in_prefix = netaddr.IPAddress(prefix.prefix.last)
|
||||||
|
|
||||||
if not records:
|
if not records:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from strawberry_django import (
|
||||||
DatetimeFilterLookup,
|
DatetimeFilterLookup,
|
||||||
FilterLookup,
|
FilterLookup,
|
||||||
RangeLookup,
|
RangeLookup,
|
||||||
|
StrFilterLookup,
|
||||||
TimeFilterLookup,
|
TimeFilterLookup,
|
||||||
process_filters,
|
process_filters,
|
||||||
)
|
)
|
||||||
|
|
@ -40,7 +41,7 @@ SKIP_MSG = 'Filter will be skipped on `null` value'
|
||||||
|
|
||||||
@strawberry.input(one_of=True, description='Lookup for JSON field. Only one of the lookup fields can be set.')
|
@strawberry.input(one_of=True, description='Lookup for JSON field. Only one of the lookup fields can be set.')
|
||||||
class JSONLookup:
|
class JSONLookup:
|
||||||
string_lookup: FilterLookup[str] | None = strawberry_django.filter_field()
|
string_lookup: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
int_range_lookup: RangeLookup[int] | 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()
|
int_comparison_lookup: ComparisonFilterLookup[int] | None = strawberry_django.filter_field()
|
||||||
float_range_lookup: RangeLookup[float] | None = strawberry_django.filter_field()
|
float_range_lookup: RangeLookup[float] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Annotated, TypeVar
|
||||||
|
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'DistanceFilterMixin',
|
'DistanceFilterMixin',
|
||||||
|
|
@ -48,7 +48,7 @@ class SyncedDataFilterMixin:
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
data_file_id: FilterLookup[int] | None = strawberry_django.filter_field()
|
data_file_id: FilterLookup[int] | None = strawberry_django.filter_field()
|
||||||
data_path: FilterLookup[str] | None = strawberry_django.filter_field()
|
data_path: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
auto_sync_enabled: FilterLookup[bool] | 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[datetime] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry import ID
|
from strawberry import ID
|
||||||
from strawberry_django import ComparisonFilterLookup, FilterLookup
|
from strawberry_django import ComparisonFilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from core.graphql.filter_mixins import ChangeLoggingMixin
|
from core.graphql.filter_mixins import ChangeLoggingMixin
|
||||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
||||||
|
|
@ -42,21 +42,21 @@ class NetBoxModelFilter(
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NestedGroupModelFilter(NetBoxModelFilter):
|
class NestedGroupModelFilter(NetBoxModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
parent_id: ID | None = strawberry_django.filter_field()
|
parent_id: ID | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class OrganizationalModelFilter(NetBoxModelFilter):
|
class OrganizationalModelFilter(NetBoxModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
comments: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PrimaryModelFilter(NetBoxModelFilter):
|
class PrimaryModelFilter(NetBoxModelFilter):
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
comments: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -32,19 +32,19 @@
|
||||||
"htmx.org": "2.0.8",
|
"htmx.org": "2.0.8",
|
||||||
"query-string": "9.3.1",
|
"query-string": "9.3.1",
|
||||||
"sass": "1.97.3",
|
"sass": "1.97.3",
|
||||||
"tom-select": "2.4.3",
|
"tom-select": "2.5.2",
|
||||||
"typeface-inter": "3.18.1",
|
"typeface-inter": "3.18.1",
|
||||||
"typeface-roboto-mono": "1.1.13"
|
"typeface-roboto-mono": "1.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^2.0.2",
|
"@eslint/compat": "^2.0.2",
|
||||||
"@eslint/eslintrc": "^3.3.3",
|
"@eslint/eslintrc": "^3.3.4",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@types/bootstrap": "5.2.10",
|
"@types/bootstrap": "5.2.10",
|
||||||
"@types/cookie": "^1.0.0",
|
"@types/cookie": "^1.0.0",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
||||||
"@typescript-eslint/parser": "^8.56.0",
|
"@typescript-eslint/parser": "^8.56.1",
|
||||||
"esbuild": "^0.27.3",
|
"esbuild": "^0.27.3",
|
||||||
"esbuild-sass-plugin": "^3.6.0",
|
"esbuild-sass-plugin": "^3.6.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-prettier": "^5.5.5",
|
"eslint-plugin-prettier": "^5.5.5",
|
||||||
"globals": "^17.3.0",
|
"globals": "^17.4.0",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { RecursivePartial, TomOption, TomSettings } from 'tom-select/dist/types/types';
|
import type { RecursivePartial, TomOption, TomSettings, TomInput } from 'tom-select/dist/cjs/types';
|
||||||
import { TomInput } from 'tom-select/dist/cjs/types/core';
|
|
||||||
import { addClasses } from 'tom-select/src/vanilla.ts';
|
import { addClasses } from 'tom-select/src/vanilla.ts';
|
||||||
import queryString from 'query-string';
|
import queryString from 'query-string';
|
||||||
import TomSelect from 'tom-select';
|
import TomSelect from 'tom-select';
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.15"
|
"@types/json-schema" "^7.0.15"
|
||||||
|
|
||||||
"@eslint/eslintrc@^3.3.1", "@eslint/eslintrc@^3.3.3":
|
"@eslint/eslintrc@^3.3.1":
|
||||||
version "3.3.3"
|
version "3.3.3"
|
||||||
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz"
|
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz"
|
||||||
integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==
|
integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==
|
||||||
|
|
@ -225,6 +225,21 @@
|
||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
|
"@eslint/eslintrc@^3.3.4":
|
||||||
|
version "3.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.4.tgz#e402b1920f7c1f5a15342caa432b1348cacbb641"
|
||||||
|
integrity sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==
|
||||||
|
dependencies:
|
||||||
|
ajv "^6.14.0"
|
||||||
|
debug "^4.3.2"
|
||||||
|
espree "^10.0.1"
|
||||||
|
globals "^14.0.0"
|
||||||
|
ignore "^5.2.0"
|
||||||
|
import-fresh "^3.2.1"
|
||||||
|
js-yaml "^4.1.1"
|
||||||
|
minimatch "^3.1.3"
|
||||||
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@eslint/js@9.39.2", "@eslint/js@^9.39.2":
|
"@eslint/js@9.39.2", "@eslint/js@^9.39.2":
|
||||||
version "9.39.2"
|
version "9.39.2"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.2.tgz#2d4b8ec4c3ea13c1b3748e0c97ecd766bdd80599"
|
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.2.tgz#2d4b8ec4c3ea13c1b3748e0c97ecd766bdd80599"
|
||||||
|
|
@ -935,100 +950,100 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "*"
|
"@types/estree" "*"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^8.56.0":
|
"@typescript-eslint/eslint-plugin@^8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz#5aec3db807a6b8437ea5d5ebf7bd16b4119aba8d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz#b1ce606d87221daec571e293009675992f0aae76"
|
||||||
integrity sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==
|
integrity sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp" "^4.12.2"
|
"@eslint-community/regexpp" "^4.12.2"
|
||||||
"@typescript-eslint/scope-manager" "8.56.0"
|
"@typescript-eslint/scope-manager" "8.56.1"
|
||||||
"@typescript-eslint/type-utils" "8.56.0"
|
"@typescript-eslint/type-utils" "8.56.1"
|
||||||
"@typescript-eslint/utils" "8.56.0"
|
"@typescript-eslint/utils" "8.56.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.56.0"
|
"@typescript-eslint/visitor-keys" "8.56.1"
|
||||||
ignore "^7.0.5"
|
ignore "^7.0.5"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
ts-api-utils "^2.4.0"
|
ts-api-utils "^2.4.0"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^8.56.0":
|
"@typescript-eslint/parser@^8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.56.0.tgz#8ecff1678b8b1a742d29c446ccf5eeea7f971d72"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.56.1.tgz#21d13b3d456ffb08614c1d68bb9a4f8d9237cdc7"
|
||||||
integrity sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==
|
integrity sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "8.56.0"
|
"@typescript-eslint/scope-manager" "8.56.1"
|
||||||
"@typescript-eslint/types" "8.56.0"
|
"@typescript-eslint/types" "8.56.1"
|
||||||
"@typescript-eslint/typescript-estree" "8.56.0"
|
"@typescript-eslint/typescript-estree" "8.56.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.56.0"
|
"@typescript-eslint/visitor-keys" "8.56.1"
|
||||||
debug "^4.4.3"
|
debug "^4.4.3"
|
||||||
|
|
||||||
"@typescript-eslint/project-service@8.56.0":
|
"@typescript-eslint/project-service@8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.56.0.tgz#bb8562fecd8f7922e676fc6a1189c20dd7991d73"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.56.1.tgz#65c8d645f028b927bfc4928593b54e2ecd809244"
|
||||||
integrity sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==
|
integrity sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/tsconfig-utils" "^8.56.0"
|
"@typescript-eslint/tsconfig-utils" "^8.56.1"
|
||||||
"@typescript-eslint/types" "^8.56.0"
|
"@typescript-eslint/types" "^8.56.1"
|
||||||
debug "^4.4.3"
|
debug "^4.4.3"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@8.56.0":
|
"@typescript-eslint/scope-manager@8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz#604030a4c6433df3728effdd441d47f45a86edb4"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz#254df93b5789a871351335dd23e20bc164060f24"
|
||||||
integrity sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==
|
integrity sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.56.0"
|
"@typescript-eslint/types" "8.56.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.56.0"
|
"@typescript-eslint/visitor-keys" "8.56.1"
|
||||||
|
|
||||||
"@typescript-eslint/tsconfig-utils@8.56.0", "@typescript-eslint/tsconfig-utils@^8.56.0":
|
"@typescript-eslint/tsconfig-utils@8.56.1", "@typescript-eslint/tsconfig-utils@^8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz#2538ce83cbc376e685487960cbb24b65fe2abc4e"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz#1afa830b0fada5865ddcabdc993b790114a879b7"
|
||||||
integrity sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==
|
integrity sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@8.56.0":
|
"@typescript-eslint/type-utils@8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz#72b4edc1fc73988998f1632b3ec99c2a66eaac6e"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz#7a6c4fabf225d674644931e004302cbbdd2f2e24"
|
||||||
integrity sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==
|
integrity sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.56.0"
|
"@typescript-eslint/types" "8.56.1"
|
||||||
"@typescript-eslint/typescript-estree" "8.56.0"
|
"@typescript-eslint/typescript-estree" "8.56.1"
|
||||||
"@typescript-eslint/utils" "8.56.0"
|
"@typescript-eslint/utils" "8.56.1"
|
||||||
debug "^4.4.3"
|
debug "^4.4.3"
|
||||||
ts-api-utils "^2.4.0"
|
ts-api-utils "^2.4.0"
|
||||||
|
|
||||||
"@typescript-eslint/types@8.56.0", "@typescript-eslint/types@^8.56.0":
|
"@typescript-eslint/types@8.56.1", "@typescript-eslint/types@^8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.56.0.tgz#a2444011b9a98ca13d70411d2cbfed5443b3526a"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.56.1.tgz#975e5942bf54895291337c91b9191f6eb0632ab9"
|
||||||
integrity sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==
|
integrity sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@8.56.0":
|
"@typescript-eslint/typescript-estree@8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz#fadbc74c14c5bac947db04980ff58bb178701c2e"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz#3b9e57d8129a860c50864c42188f761bdef3eab0"
|
||||||
integrity sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==
|
integrity sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/project-service" "8.56.0"
|
"@typescript-eslint/project-service" "8.56.1"
|
||||||
"@typescript-eslint/tsconfig-utils" "8.56.0"
|
"@typescript-eslint/tsconfig-utils" "8.56.1"
|
||||||
"@typescript-eslint/types" "8.56.0"
|
"@typescript-eslint/types" "8.56.1"
|
||||||
"@typescript-eslint/visitor-keys" "8.56.0"
|
"@typescript-eslint/visitor-keys" "8.56.1"
|
||||||
debug "^4.4.3"
|
debug "^4.4.3"
|
||||||
minimatch "^9.0.5"
|
minimatch "^10.2.2"
|
||||||
semver "^7.7.3"
|
semver "^7.7.3"
|
||||||
tinyglobby "^0.2.15"
|
tinyglobby "^0.2.15"
|
||||||
ts-api-utils "^2.4.0"
|
ts-api-utils "^2.4.0"
|
||||||
|
|
||||||
"@typescript-eslint/utils@8.56.0":
|
"@typescript-eslint/utils@8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.56.0.tgz#063ce6f702ec603de1b83ee795ed5e877d6f7841"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.56.1.tgz#5a86acaf9f1b4c4a85a42effb217f73059f6deb7"
|
||||||
integrity sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==
|
integrity sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.9.1"
|
"@eslint-community/eslint-utils" "^4.9.1"
|
||||||
"@typescript-eslint/scope-manager" "8.56.0"
|
"@typescript-eslint/scope-manager" "8.56.1"
|
||||||
"@typescript-eslint/types" "8.56.0"
|
"@typescript-eslint/types" "8.56.1"
|
||||||
"@typescript-eslint/typescript-estree" "8.56.0"
|
"@typescript-eslint/typescript-estree" "8.56.1"
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@8.56.0":
|
"@typescript-eslint/visitor-keys@8.56.1":
|
||||||
version "8.56.0"
|
version "8.56.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz#7d6592ab001827d3ce052155edf7ecad19688d7d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz#50e03475c33a42d123dc99e63acf1841c0231f87"
|
||||||
integrity sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==
|
integrity sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "8.56.0"
|
"@typescript-eslint/types" "8.56.1"
|
||||||
eslint-visitor-keys "^5.0.0"
|
eslint-visitor-keys "^5.0.0"
|
||||||
|
|
||||||
"@unrs/resolver-binding-android-arm-eabi@1.11.1":
|
"@unrs/resolver-binding-android-arm-eabi@1.11.1":
|
||||||
|
|
@ -1148,6 +1163,16 @@ ajv@^6.12.4:
|
||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
ajv@^6.14.0:
|
||||||
|
version "6.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a"
|
||||||
|
integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal "^3.1.1"
|
||||||
|
fast-json-stable-stringify "^2.0.0"
|
||||||
|
json-schema-traverse "^0.4.1"
|
||||||
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ansi-styles@^4.1.0:
|
ansi-styles@^4.1.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
|
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
|
||||||
|
|
@ -1274,6 +1299,11 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
balanced-match@^4.0.2:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
|
||||||
|
integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
|
||||||
|
|
||||||
bootstrap@5.3.7:
|
bootstrap@5.3.7:
|
||||||
version "5.3.7"
|
version "5.3.7"
|
||||||
resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz"
|
resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz"
|
||||||
|
|
@ -1292,12 +1322,12 @@ brace-expansion@^1.1.7:
|
||||||
balanced-match "^1.0.0"
|
balanced-match "^1.0.0"
|
||||||
concat-map "0.0.1"
|
concat-map "0.0.1"
|
||||||
|
|
||||||
brace-expansion@^2.0.1:
|
brace-expansion@^5.0.2:
|
||||||
version "2.0.2"
|
version "5.0.4"
|
||||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz"
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336"
|
||||||
integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
|
integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match "^1.0.0"
|
balanced-match "^4.0.2"
|
||||||
|
|
||||||
braces@^3.0.3:
|
braces@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
|
|
@ -2189,10 +2219,10 @@ globals@^14.0.0:
|
||||||
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
||||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
||||||
|
|
||||||
globals@^17.3.0:
|
globals@^17.4.0:
|
||||||
version "17.3.0"
|
version "17.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-17.3.0.tgz#8b96544c2fa91afada02747cc9731c002a96f3b9"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-17.4.0.tgz#33d7d297ed1536b388a0e2f4bcd0ff19c8ff91b5"
|
||||||
integrity sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==
|
integrity sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==
|
||||||
|
|
||||||
globalthis@^1.0.3, globalthis@^1.0.4:
|
globalthis@^1.0.3, globalthis@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
|
|
@ -2784,6 +2814,13 @@ micromatch@^4.0.5:
|
||||||
braces "^3.0.3"
|
braces "^3.0.3"
|
||||||
picomatch "^2.3.1"
|
picomatch "^2.3.1"
|
||||||
|
|
||||||
|
minimatch@^10.2.2:
|
||||||
|
version "10.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde"
|
||||||
|
integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^5.0.2"
|
||||||
|
|
||||||
minimatch@^3.1.2:
|
minimatch@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
||||||
|
|
@ -2791,12 +2828,12 @@ minimatch@^3.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimatch@^9.0.5:
|
minimatch@^3.1.3:
|
||||||
version "9.0.5"
|
version "3.1.5"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e"
|
||||||
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
|
integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^2.0.1"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@^1.2.6:
|
minimist@^1.2.0, minimist@^1.2.6:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
|
|
@ -3455,10 +3492,10 @@ toggle-selection@^1.0.6:
|
||||||
resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz"
|
resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz"
|
||||||
integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==
|
integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==
|
||||||
|
|
||||||
tom-select@2.4.3:
|
tom-select@2.5.2:
|
||||||
version "2.4.3"
|
version "2.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.4.3.tgz#1daa4131cd317de691f39eb5bf41148265986c1f"
|
resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.5.2.tgz#77dd4bc780b1ea72905337b24f04ce19dc6d2ca1"
|
||||||
integrity sha512-MFFrMxP1bpnAMPbdvPCZk0KwYxLqhYZso39torcdoefeV/NThNyDu8dV96/INJ5XQVTL3O55+GqQ78Pkj5oCfw==
|
integrity sha512-VAlGj5MBWVLMJje2NwA3XSmxa7CUFpp1tdzFZ8wymCkcLeP0NwF4ARmSuUK4BWbmSN1fETlSazWkMIxEpP4GdQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@orchidjs/sifter" "^1.1.0"
|
"@orchidjs/sifter" "^1.1.0"
|
||||||
"@orchidjs/unicode-variants" "^1.1.2"
|
"@orchidjs/unicode-variants" "^1.1.2"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version: "4.5.3"
|
version: "4.5.4"
|
||||||
edition: "Community"
|
edition: "Community"
|
||||||
published: "2026-02-17"
|
published: "2026-03-03"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<a href="{{ value.get_absolute_url }}"{% if name %} id="attr_{{ name }}"{% endif %}>{{ value.address.ip }}</a>
|
<span>
|
||||||
{% if value.nat_inside %}
|
<a href="{{ value.get_absolute_url }}"{% if name %} id="attr_{{ name }}"{% endif %}>{{ value.address.ip }}</a>
|
||||||
({% trans "NAT for" %} <a href="{{ value.nat_inside.get_absolute_url }}">{{ value.nat_inside.address.ip }}</a>)
|
{% if value.nat_inside %}
|
||||||
{% elif value.nat_outside.exists %}
|
({% trans "NAT for" %} <a href="{{ value.nat_inside.get_absolute_url }}">{{ value.nat_inside.address.ip }}</a>)
|
||||||
({% trans "NAT" %}: {% for nat in value.nat_outside.all %}<a href="{{ nat.get_absolute_url }}">{{ nat.address.ip }}</a>{% if not forloop.last %}, {% endif %}{% endfor %})
|
{% elif value.nat_outside.exists %}
|
||||||
{% endif %}
|
({% trans "NAT" %}: {% for nat in value.nat_outside.all %}<a href="{{ nat.get_absolute_url }}">{{ nat.address.ip }}</a>{% if not forloop.last %}, {% endif %}{% endfor %})
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
<a class="btn btn-sm btn-primary copy-content" data-clipboard-target="#attr_{{ name }}" title="{% trans "Copy to clipboard" %}">
|
<a class="btn btn-sm btn-primary copy-content" data-clipboard-target="#attr_{{ name }}" title="{% trans "Copy to clipboard" %}">
|
||||||
<i class="mdi mdi-content-copy"></i>
|
<i class="mdi mdi-content-copy"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
||||||
from netbox.graphql.filters import (
|
from netbox.graphql.filters import (
|
||||||
|
|
@ -60,8 +60,8 @@ __all__ = (
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Tenant, lookups=True)
|
@strawberry_django.filter_type(models.Tenant, lookups=True)
|
||||||
class TenantFilter(ContactFilterMixin, PrimaryModelFilter):
|
class TenantFilter(ContactFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -153,12 +153,12 @@ class TenantGroupFilter(OrganizationalModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Contact, lookups=True)
|
@strawberry_django.filter_type(models.Contact, lookups=True)
|
||||||
class ContactFilter(PrimaryModelFilter):
|
class ContactFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
title: FilterLookup[str] | None = strawberry_django.filter_field()
|
title: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
phone: FilterLookup[str] | None = strawberry_django.filter_field()
|
phone: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
email: FilterLookup[str] | None = strawberry_django.filter_field()
|
email: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
address: FilterLookup[str] | None = strawberry_django.filter_field()
|
address: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
link: FilterLookup[str] | None = strawberry_django.filter_field()
|
link: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
groups: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
groups: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-02-21 05:16+0000\n"
|
"POT-Creation-Date: 2026-03-03 05:20+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -41,9 +41,9 @@ msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/choices.py:21 netbox/dcim/choices.py:20
|
#: netbox/circuits/choices.py:21 netbox/dcim/choices.py:20
|
||||||
#: netbox/dcim/choices.py:102 netbox/dcim/choices.py:204
|
#: netbox/dcim/choices.py:102 netbox/dcim/choices.py:204
|
||||||
#: netbox/dcim/choices.py:257 netbox/dcim/choices.py:1929
|
#: netbox/dcim/choices.py:257 netbox/dcim/choices.py:1933
|
||||||
#: netbox/dcim/choices.py:1987 netbox/dcim/choices.py:2054
|
#: netbox/dcim/choices.py:1991 netbox/dcim/choices.py:2058
|
||||||
#: netbox/dcim/choices.py:2076 netbox/virtualization/choices.py:20
|
#: netbox/dcim/choices.py:2080 netbox/virtualization/choices.py:20
|
||||||
#: netbox/virtualization/choices.py:46 netbox/vpn/choices.py:18
|
#: netbox/virtualization/choices.py:46 netbox/vpn/choices.py:18
|
||||||
#: netbox/vpn/choices.py:281
|
#: netbox/vpn/choices.py:281
|
||||||
msgid "Planned"
|
msgid "Planned"
|
||||||
|
|
@ -57,8 +57,8 @@ msgstr ""
|
||||||
#: netbox/core/tables/tasks.py:23 netbox/dcim/choices.py:22
|
#: netbox/core/tables/tasks.py:23 netbox/dcim/choices.py:22
|
||||||
#: netbox/dcim/choices.py:103 netbox/dcim/choices.py:155
|
#: netbox/dcim/choices.py:103 netbox/dcim/choices.py:155
|
||||||
#: netbox/dcim/choices.py:203 netbox/dcim/choices.py:256
|
#: netbox/dcim/choices.py:203 netbox/dcim/choices.py:256
|
||||||
#: netbox/dcim/choices.py:1986 netbox/dcim/choices.py:2053
|
#: netbox/dcim/choices.py:1990 netbox/dcim/choices.py:2057
|
||||||
#: netbox/dcim/choices.py:2075 netbox/extras/tables/tables.py:642
|
#: netbox/dcim/choices.py:2079 netbox/extras/tables/tables.py:642
|
||||||
#: netbox/ipam/choices.py:31 netbox/ipam/choices.py:49
|
#: netbox/ipam/choices.py:31 netbox/ipam/choices.py:49
|
||||||
#: netbox/ipam/choices.py:69 netbox/ipam/choices.py:154
|
#: netbox/ipam/choices.py:69 netbox/ipam/choices.py:154
|
||||||
#: netbox/templates/extras/configcontext.html:29
|
#: netbox/templates/extras/configcontext.html:29
|
||||||
|
|
@ -70,8 +70,8 @@ msgid "Active"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/choices.py:24 netbox/dcim/choices.py:202
|
#: netbox/circuits/choices.py:24 netbox/dcim/choices.py:202
|
||||||
#: netbox/dcim/choices.py:255 netbox/dcim/choices.py:1985
|
#: netbox/dcim/choices.py:255 netbox/dcim/choices.py:1989
|
||||||
#: netbox/dcim/choices.py:2055 netbox/dcim/choices.py:2074
|
#: netbox/dcim/choices.py:2059 netbox/dcim/choices.py:2078
|
||||||
#: netbox/virtualization/choices.py:24 netbox/virtualization/choices.py:44
|
#: netbox/virtualization/choices.py:24 netbox/virtualization/choices.py:44
|
||||||
msgid "Offline"
|
msgid "Offline"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -84,7 +84,7 @@ msgstr ""
|
||||||
msgid "Decommissioned"
|
msgid "Decommissioned"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1998
|
#: netbox/circuits/choices.py:90 netbox/dcim/choices.py:2002
|
||||||
#: netbox/dcim/tables/devices.py:1208 netbox/templates/dcim/interface.html:148
|
#: netbox/dcim/tables/devices.py:1208 netbox/templates/dcim/interface.html:148
|
||||||
#: netbox/tenancy/choices.py:17
|
#: netbox/tenancy/choices.py:17
|
||||||
msgid "Primary"
|
msgid "Primary"
|
||||||
|
|
@ -1995,7 +1995,7 @@ msgstr ""
|
||||||
#: netbox/core/choices.py:22 netbox/core/choices.py:59
|
#: netbox/core/choices.py:22 netbox/core/choices.py:59
|
||||||
#: netbox/core/constants.py:21 netbox/core/tables/tasks.py:35
|
#: netbox/core/constants.py:21 netbox/core/tables/tasks.py:35
|
||||||
#: netbox/dcim/choices.py:206 netbox/dcim/choices.py:259
|
#: netbox/dcim/choices.py:206 netbox/dcim/choices.py:259
|
||||||
#: netbox/dcim/choices.py:1988 netbox/dcim/choices.py:2078
|
#: netbox/dcim/choices.py:1992 netbox/dcim/choices.py:2082
|
||||||
#: netbox/virtualization/choices.py:48
|
#: netbox/virtualization/choices.py:48
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -2181,7 +2181,7 @@ msgid "User name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/forms/bulk_edit.py:25 netbox/core/forms/filtersets.py:47
|
#: netbox/core/forms/bulk_edit.py:25 netbox/core/forms/filtersets.py:47
|
||||||
#: netbox/core/tables/data.py:28 netbox/dcim/choices.py:2036
|
#: netbox/core/tables/data.py:28 netbox/dcim/choices.py:2040
|
||||||
#: netbox/dcim/forms/bulk_edit.py:1105 netbox/dcim/forms/bulk_edit.py:1386
|
#: netbox/dcim/forms/bulk_edit.py:1105 netbox/dcim/forms/bulk_edit.py:1386
|
||||||
#: netbox/dcim/forms/filtersets.py:1619 netbox/dcim/forms/filtersets.py:1712
|
#: netbox/dcim/forms/filtersets.py:1619 netbox/dcim/forms/filtersets.py:1712
|
||||||
#: netbox/dcim/tables/devices.py:581 netbox/dcim/tables/devicetypes.py:233
|
#: netbox/dcim/tables/devices.py:581 netbox/dcim/tables/devicetypes.py:233
|
||||||
|
|
@ -2375,7 +2375,7 @@ msgstr ""
|
||||||
msgid "Rack Elevations"
|
msgid "Rack Elevations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/forms/model_forms.py:160 netbox/dcim/choices.py:1907
|
#: netbox/core/forms/model_forms.py:160 netbox/dcim/choices.py:1911
|
||||||
#: netbox/dcim/forms/bulk_edit.py:944 netbox/dcim/forms/bulk_edit.py:1340
|
#: netbox/dcim/forms/bulk_edit.py:944 netbox/dcim/forms/bulk_edit.py:1340
|
||||||
#: netbox/dcim/forms/bulk_edit.py:1361 netbox/dcim/tables/racks.py:144
|
#: netbox/dcim/forms/bulk_edit.py:1361 netbox/dcim/tables/racks.py:144
|
||||||
#: netbox/netbox/navigation/menu.py:316 netbox/netbox/navigation/menu.py:320
|
#: netbox/netbox/navigation/menu.py:316 netbox/netbox/navigation/menu.py:320
|
||||||
|
|
@ -3041,8 +3041,8 @@ msgid "Staging"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:23 netbox/dcim/choices.py:208
|
#: netbox/dcim/choices.py:23 netbox/dcim/choices.py:208
|
||||||
#: netbox/dcim/choices.py:260 netbox/dcim/choices.py:1930
|
#: netbox/dcim/choices.py:260 netbox/dcim/choices.py:1934
|
||||||
#: netbox/dcim/choices.py:2079 netbox/virtualization/choices.py:23
|
#: netbox/dcim/choices.py:2083 netbox/virtualization/choices.py:23
|
||||||
#: netbox/virtualization/choices.py:49 netbox/vpn/choices.py:282
|
#: netbox/virtualization/choices.py:49 netbox/vpn/choices.py:282
|
||||||
msgid "Decommissioning"
|
msgid "Decommissioning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -3108,7 +3108,7 @@ msgstr ""
|
||||||
msgid "Millimeters"
|
msgid "Millimeters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:115 netbox/dcim/choices.py:1952
|
#: netbox/dcim/choices.py:115 netbox/dcim/choices.py:1956
|
||||||
msgid "Inches"
|
msgid "Inches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -3186,7 +3186,7 @@ msgid "Rear"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:205 netbox/dcim/choices.py:258
|
#: netbox/dcim/choices.py:205 netbox/dcim/choices.py:258
|
||||||
#: netbox/dcim/choices.py:2077 netbox/virtualization/choices.py:47
|
#: netbox/dcim/choices.py:2081 netbox/virtualization/choices.py:47
|
||||||
msgid "Staged"
|
msgid "Staged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -3219,7 +3219,7 @@ msgid "Top to bottom"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:235 netbox/dcim/choices.py:280
|
#: netbox/dcim/choices.py:235 netbox/dcim/choices.py:280
|
||||||
#: netbox/dcim/choices.py:1562
|
#: netbox/dcim/choices.py:1566
|
||||||
msgid "Passive"
|
msgid "Passive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -3248,8 +3248,8 @@ msgid "Proprietary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:606 netbox/dcim/choices.py:853
|
#: netbox/dcim/choices.py:606 netbox/dcim/choices.py:853
|
||||||
#: netbox/dcim/choices.py:1474 netbox/dcim/choices.py:1476
|
#: netbox/dcim/choices.py:1478 netbox/dcim/choices.py:1480
|
||||||
#: netbox/dcim/choices.py:1712 netbox/dcim/choices.py:1714
|
#: netbox/dcim/choices.py:1716 netbox/dcim/choices.py:1718
|
||||||
#: netbox/netbox/navigation/menu.py:212
|
#: netbox/netbox/navigation/menu.py:212
|
||||||
msgid "Other"
|
msgid "Other"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -3262,11 +3262,11 @@ msgstr ""
|
||||||
msgid "Physical"
|
msgid "Physical"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:884 netbox/dcim/choices.py:1151
|
#: netbox/dcim/choices.py:884 netbox/dcim/choices.py:1153
|
||||||
msgid "Virtual"
|
msgid "Virtual"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:885 netbox/dcim/choices.py:1351
|
#: netbox/dcim/choices.py:885 netbox/dcim/choices.py:1355
|
||||||
#: netbox/dcim/forms/bulk_edit.py:1546 netbox/dcim/forms/filtersets.py:1577
|
#: netbox/dcim/forms/bulk_edit.py:1546 netbox/dcim/forms/filtersets.py:1577
|
||||||
#: netbox/dcim/forms/filtersets.py:1703 netbox/dcim/forms/model_forms.py:1125
|
#: netbox/dcim/forms/filtersets.py:1703 netbox/dcim/forms/model_forms.py:1125
|
||||||
#: netbox/dcim/forms/model_forms.py:1589 netbox/netbox/navigation/menu.py:150
|
#: netbox/dcim/forms/model_forms.py:1589 netbox/netbox/navigation/menu.py:150
|
||||||
|
|
@ -3275,11 +3275,11 @@ msgstr ""
|
||||||
msgid "Wireless"
|
msgid "Wireless"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1149
|
#: netbox/dcim/choices.py:1151
|
||||||
msgid "Virtual interfaces"
|
msgid "Virtual interfaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1152 netbox/dcim/forms/bulk_edit.py:1399
|
#: netbox/dcim/choices.py:1154 netbox/dcim/forms/bulk_edit.py:1399
|
||||||
#: netbox/dcim/forms/bulk_import.py:949 netbox/dcim/forms/model_forms.py:1107
|
#: netbox/dcim/forms/bulk_import.py:949 netbox/dcim/forms/model_forms.py:1107
|
||||||
#: netbox/dcim/tables/devices.py:741 netbox/templates/dcim/interface.html:112
|
#: netbox/dcim/tables/devices.py:741 netbox/templates/dcim/interface.html:112
|
||||||
#: netbox/virtualization/forms/bulk_edit.py:177
|
#: netbox/virtualization/forms/bulk_edit.py:177
|
||||||
|
|
@ -3288,67 +3288,67 @@ msgstr ""
|
||||||
msgid "Bridge"
|
msgid "Bridge"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1153
|
#: netbox/dcim/choices.py:1155
|
||||||
msgid "Link Aggregation Group (LAG)"
|
msgid "Link Aggregation Group (LAG)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1157
|
#: netbox/dcim/choices.py:1159
|
||||||
msgid "FastEthernet (100 Mbps)"
|
msgid "FastEthernet (100 Mbps)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1166
|
#: netbox/dcim/choices.py:1168
|
||||||
msgid "GigabitEthernet (1 Gbps)"
|
msgid "GigabitEthernet (1 Gbps)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1184
|
#: netbox/dcim/choices.py:1186
|
||||||
msgid "2.5/5 Gbps Ethernet"
|
msgid "2.5/5 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1191
|
#: netbox/dcim/choices.py:1193
|
||||||
msgid "10 Gbps Ethernet"
|
msgid "10 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1206
|
#: netbox/dcim/choices.py:1209
|
||||||
msgid "25 Gbps Ethernet"
|
msgid "25 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1216
|
#: netbox/dcim/choices.py:1219
|
||||||
msgid "40 Gbps Ethernet"
|
msgid "40 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1226
|
#: netbox/dcim/choices.py:1230
|
||||||
msgid "50 Gbps Ethernet"
|
msgid "50 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1236
|
#: netbox/dcim/choices.py:1240
|
||||||
msgid "100 Gbps Ethernet"
|
msgid "100 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1257
|
#: netbox/dcim/choices.py:1261
|
||||||
msgid "200 Gbps Ethernet"
|
msgid "200 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1271
|
#: netbox/dcim/choices.py:1275
|
||||||
msgid "400 Gbps Ethernet"
|
msgid "400 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1289
|
#: netbox/dcim/choices.py:1293
|
||||||
msgid "800 Gbps Ethernet"
|
msgid "800 Gbps Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1298
|
#: netbox/dcim/choices.py:1302
|
||||||
msgid "Pluggable transceivers"
|
msgid "Pluggable transceivers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1335
|
#: netbox/dcim/choices.py:1339
|
||||||
msgid "Backplane Ethernet"
|
msgid "Backplane Ethernet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1367
|
#: netbox/dcim/choices.py:1371
|
||||||
msgid "Cellular"
|
msgid "Cellular"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1419 netbox/dcim/forms/filtersets.py:425
|
#: netbox/dcim/choices.py:1423 netbox/dcim/forms/filtersets.py:425
|
||||||
#: netbox/dcim/forms/filtersets.py:911 netbox/dcim/forms/filtersets.py:1112
|
#: netbox/dcim/forms/filtersets.py:911 netbox/dcim/forms/filtersets.py:1112
|
||||||
#: netbox/dcim/forms/filtersets.py:1910
|
#: netbox/dcim/forms/filtersets.py:1910
|
||||||
#: netbox/templates/dcim/inventoryitem.html:56
|
#: netbox/templates/dcim/inventoryitem.html:56
|
||||||
|
|
@ -3356,255 +3356,255 @@ msgstr ""
|
||||||
msgid "Serial"
|
msgid "Serial"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1434
|
#: netbox/dcim/choices.py:1438
|
||||||
msgid "Coaxial"
|
msgid "Coaxial"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1455
|
#: netbox/dcim/choices.py:1459
|
||||||
msgid "Stacking"
|
msgid "Stacking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1507
|
#: netbox/dcim/choices.py:1511
|
||||||
msgid "Half"
|
msgid "Half"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1508
|
#: netbox/dcim/choices.py:1512
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1509 netbox/netbox/preferences.py:32
|
#: netbox/dcim/choices.py:1513 netbox/netbox/preferences.py:32
|
||||||
#: netbox/wireless/choices.py:480
|
#: netbox/wireless/choices.py:480
|
||||||
msgid "Auto"
|
msgid "Auto"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1521
|
#: netbox/dcim/choices.py:1525
|
||||||
msgid "Access"
|
msgid "Access"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1522 netbox/ipam/tables/vlans.py:150
|
#: netbox/dcim/choices.py:1526 netbox/ipam/tables/vlans.py:150
|
||||||
#: netbox/ipam/tables/vlans.py:210
|
#: netbox/ipam/tables/vlans.py:210
|
||||||
#: netbox/templates/dcim/inc/interface_vlans_table.html:7
|
#: netbox/templates/dcim/inc/interface_vlans_table.html:7
|
||||||
msgid "Tagged"
|
msgid "Tagged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1523
|
#: netbox/dcim/choices.py:1527
|
||||||
msgid "Tagged (All)"
|
msgid "Tagged (All)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1524 netbox/templates/ipam/vlan_edit.html:26
|
#: netbox/dcim/choices.py:1528 netbox/templates/ipam/vlan_edit.html:26
|
||||||
msgid "Q-in-Q (802.1ad)"
|
msgid "Q-in-Q (802.1ad)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1553
|
#: netbox/dcim/choices.py:1557
|
||||||
msgid "IEEE Standard"
|
msgid "IEEE Standard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1564
|
#: netbox/dcim/choices.py:1568
|
||||||
msgid "Passive 24V (2-pair)"
|
msgid "Passive 24V (2-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1565
|
#: netbox/dcim/choices.py:1569
|
||||||
msgid "Passive 24V (4-pair)"
|
msgid "Passive 24V (4-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1566
|
#: netbox/dcim/choices.py:1570
|
||||||
msgid "Passive 48V (2-pair)"
|
msgid "Passive 48V (2-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1567
|
#: netbox/dcim/choices.py:1571
|
||||||
msgid "Passive 48V (4-pair)"
|
msgid "Passive 48V (4-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1640
|
#: netbox/dcim/choices.py:1644
|
||||||
msgid "Copper"
|
msgid "Copper"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1663
|
#: netbox/dcim/choices.py:1667
|
||||||
msgid "Fiber Optic"
|
msgid "Fiber Optic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1699 netbox/dcim/choices.py:1913
|
#: netbox/dcim/choices.py:1703 netbox/dcim/choices.py:1917
|
||||||
msgid "USB"
|
msgid "USB"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1755
|
#: netbox/dcim/choices.py:1759
|
||||||
msgid "Single"
|
msgid "Single"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1757
|
#: netbox/dcim/choices.py:1761
|
||||||
msgid "1C1P"
|
msgid "1C1P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1758
|
#: netbox/dcim/choices.py:1762
|
||||||
msgid "1C2P"
|
msgid "1C2P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1759
|
#: netbox/dcim/choices.py:1763
|
||||||
msgid "1C4P"
|
msgid "1C4P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1760
|
#: netbox/dcim/choices.py:1764
|
||||||
msgid "1C6P"
|
msgid "1C6P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1761
|
#: netbox/dcim/choices.py:1765
|
||||||
msgid "1C8P"
|
msgid "1C8P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1762
|
#: netbox/dcim/choices.py:1766
|
||||||
msgid "1C12P"
|
msgid "1C12P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1763
|
#: netbox/dcim/choices.py:1767
|
||||||
msgid "1C16P"
|
msgid "1C16P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1767
|
#: netbox/dcim/choices.py:1771
|
||||||
msgid "Trunk"
|
msgid "Trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1769
|
#: netbox/dcim/choices.py:1773
|
||||||
msgid "2C1P trunk"
|
msgid "2C1P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1770
|
#: netbox/dcim/choices.py:1774
|
||||||
msgid "2C2P trunk"
|
msgid "2C2P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1771
|
#: netbox/dcim/choices.py:1775
|
||||||
msgid "2C4P trunk"
|
msgid "2C4P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1772
|
#: netbox/dcim/choices.py:1776
|
||||||
msgid "2C4P trunk (shuffle)"
|
msgid "2C4P trunk (shuffle)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1773
|
#: netbox/dcim/choices.py:1777
|
||||||
msgid "2C6P trunk"
|
msgid "2C6P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1774
|
#: netbox/dcim/choices.py:1778
|
||||||
msgid "2C8P trunk"
|
msgid "2C8P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1775
|
#: netbox/dcim/choices.py:1779
|
||||||
msgid "2C12P trunk"
|
msgid "2C12P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1776
|
#: netbox/dcim/choices.py:1780
|
||||||
msgid "4C1P trunk"
|
msgid "4C1P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1777
|
#: netbox/dcim/choices.py:1781
|
||||||
msgid "4C2P trunk"
|
msgid "4C2P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1778
|
#: netbox/dcim/choices.py:1782
|
||||||
msgid "4C4P trunk"
|
msgid "4C4P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1779
|
#: netbox/dcim/choices.py:1783
|
||||||
msgid "4C4P trunk (shuffle)"
|
msgid "4C4P trunk (shuffle)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1780
|
#: netbox/dcim/choices.py:1784
|
||||||
msgid "4C6P trunk"
|
msgid "4C6P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1781
|
#: netbox/dcim/choices.py:1785
|
||||||
msgid "4C8P trunk"
|
msgid "4C8P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1782
|
#: netbox/dcim/choices.py:1786
|
||||||
msgid "8C4P trunk"
|
msgid "8C4P trunk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1786
|
#: netbox/dcim/choices.py:1790
|
||||||
msgid "Breakout"
|
msgid "Breakout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1788
|
#: netbox/dcim/choices.py:1792
|
||||||
msgid "1C4P:4C1P breakout"
|
msgid "1C4P:4C1P breakout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1789
|
#: netbox/dcim/choices.py:1793
|
||||||
msgid "1C6P:6C1P breakout"
|
msgid "1C6P:6C1P breakout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1790
|
#: netbox/dcim/choices.py:1794
|
||||||
msgid "2C4P:8C1P breakout (shuffle)"
|
msgid "2C4P:8C1P breakout (shuffle)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1848
|
#: netbox/dcim/choices.py:1852
|
||||||
msgid "Copper - Twisted Pair (UTP/STP)"
|
msgid "Copper - Twisted Pair (UTP/STP)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1862
|
#: netbox/dcim/choices.py:1866
|
||||||
msgid "Copper - Twinax (DAC)"
|
msgid "Copper - Twinax (DAC)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1869
|
#: netbox/dcim/choices.py:1873
|
||||||
msgid "Copper - Coaxial"
|
msgid "Copper - Coaxial"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1884
|
#: netbox/dcim/choices.py:1888
|
||||||
msgid "Fiber - Multimode"
|
msgid "Fiber - Multimode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1895
|
#: netbox/dcim/choices.py:1899
|
||||||
msgid "Fiber - Single-mode"
|
msgid "Fiber - Single-mode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1903
|
#: netbox/dcim/choices.py:1907
|
||||||
msgid "Fiber - Other"
|
msgid "Fiber - Other"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1928 netbox/dcim/forms/filtersets.py:1402
|
#: netbox/dcim/choices.py:1932 netbox/dcim/forms/filtersets.py:1402
|
||||||
msgid "Connected"
|
msgid "Connected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1947 netbox/netbox/choices.py:177
|
#: netbox/dcim/choices.py:1951 netbox/netbox/choices.py:177
|
||||||
msgid "Kilometers"
|
msgid "Kilometers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1948 netbox/netbox/choices.py:178
|
#: netbox/dcim/choices.py:1952 netbox/netbox/choices.py:178
|
||||||
#: netbox/templates/dcim/cable_trace.html:65
|
#: netbox/templates/dcim/cable_trace.html:65
|
||||||
msgid "Meters"
|
msgid "Meters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1949
|
#: netbox/dcim/choices.py:1953
|
||||||
msgid "Centimeters"
|
msgid "Centimeters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1950 netbox/netbox/choices.py:179
|
#: netbox/dcim/choices.py:1954 netbox/netbox/choices.py:179
|
||||||
msgid "Miles"
|
msgid "Miles"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1951 netbox/netbox/choices.py:180
|
#: netbox/dcim/choices.py:1955 netbox/netbox/choices.py:180
|
||||||
#: netbox/templates/dcim/cable_trace.html:66
|
#: netbox/templates/dcim/cable_trace.html:66
|
||||||
msgid "Feet"
|
msgid "Feet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1999
|
#: netbox/dcim/choices.py:2003
|
||||||
msgid "Redundant"
|
msgid "Redundant"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:2020
|
#: netbox/dcim/choices.py:2024
|
||||||
msgid "Single phase"
|
msgid "Single phase"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:2021
|
#: netbox/dcim/choices.py:2025
|
||||||
msgid "Three-phase"
|
msgid "Three-phase"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:2037 netbox/extras/choices.py:53
|
#: netbox/dcim/choices.py:2041 netbox/extras/choices.py:53
|
||||||
#: netbox/netbox/preferences.py:45 netbox/netbox/preferences.py:70
|
#: netbox/netbox/preferences.py:45 netbox/netbox/preferences.py:70
|
||||||
#: netbox/templates/extras/customfield.html:78 netbox/vpn/choices.py:20
|
#: netbox/templates/extras/customfield.html:78 netbox/vpn/choices.py:20
|
||||||
#: netbox/wireless/choices.py:27
|
#: netbox/wireless/choices.py:27
|
||||||
msgid "Disabled"
|
msgid "Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:2038
|
#: netbox/dcim/choices.py:2042
|
||||||
msgid "Faulty"
|
msgid "Faulty"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -13824,17 +13824,17 @@ msgstr ""
|
||||||
msgid "Not Connected"
|
msgid "Not Connected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/dcim/device/attrs/ipaddress.html:4
|
#: netbox/templates/dcim/device/attrs/ipaddress.html:5
|
||||||
#: netbox/templates/virtualization/virtualmachine/attrs/ipaddress.html:4
|
#: netbox/templates/virtualization/virtualmachine/attrs/ipaddress.html:4
|
||||||
msgid "NAT for"
|
msgid "NAT for"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/dcim/device/attrs/ipaddress.html:6
|
#: netbox/templates/dcim/device/attrs/ipaddress.html:7
|
||||||
#: netbox/templates/virtualization/virtualmachine/attrs/ipaddress.html:6
|
#: netbox/templates/virtualization/virtualmachine/attrs/ipaddress.html:6
|
||||||
msgid "NAT"
|
msgid "NAT"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/dcim/device/attrs/ipaddress.html:8
|
#: netbox/templates/dcim/device/attrs/ipaddress.html:10
|
||||||
#: netbox/templates/ui/actions/copy_content.html:2
|
#: netbox/templates/ui/actions/copy_content.html:2
|
||||||
#: netbox/templates/ui/attrs/numeric.html:9
|
#: netbox/templates/ui/attrs/numeric.html:9
|
||||||
#: netbox/templates/ui/attrs/text.html:4
|
#: netbox/templates/ui/attrs/text.html:4
|
||||||
|
|
@ -16677,7 +16677,7 @@ msgstr ""
|
||||||
msgid "A column named {name} is already defined for table {table_name}"
|
msgid "A column named {name} is already defined for table {table_name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/templates/builtins/customfield_value.html:30
|
#: netbox/utilities/templates/builtins/customfield_value.html:32
|
||||||
msgid "Not defined"
|
msgid "Not defined"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -3,7 +3,7 @@ from typing import Annotated
|
||||||
|
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry_django import DatetimeFilterLookup, FilterLookup
|
from strawberry_django import DatetimeFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from netbox.graphql.filters import BaseModelFilter
|
from netbox.graphql.filters import BaseModelFilter
|
||||||
from users import models
|
from users import models
|
||||||
|
|
@ -18,16 +18,16 @@ __all__ = (
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Group, lookups=True)
|
@strawberry_django.filter_type(models.Group, lookups=True)
|
||||||
class GroupFilter(BaseModelFilter):
|
class GroupFilter(BaseModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.User, lookups=True)
|
@strawberry_django.filter_type(models.User, lookups=True)
|
||||||
class UserFilter(BaseModelFilter):
|
class UserFilter(BaseModelFilter):
|
||||||
username: FilterLookup[str] | None = strawberry_django.filter_field()
|
username: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
first_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
first_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
last_name: FilterLookup[str] | None = strawberry_django.filter_field()
|
last_name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
email: FilterLookup[str] | None = strawberry_django.filter_field()
|
email: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
is_superuser: FilterLookup[bool] | None = strawberry_django.filter_field()
|
is_superuser: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
|
is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||||
date_joined: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
|
date_joined: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
|
||||||
|
|
@ -37,8 +37,8 @@ class UserFilter(BaseModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Owner, lookups=True)
|
@strawberry_django.filter_type(models.Owner, lookups=True)
|
||||||
class OwnerFilter(BaseModelFilter):
|
class OwnerFilter(BaseModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
group: Annotated['OwnerGroupFilter', strawberry.lazy('users.graphql.filters')] | None = (
|
group: Annotated['OwnerGroupFilter', strawberry.lazy('users.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -50,5 +50,5 @@ class OwnerFilter(BaseModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.OwnerGroup, lookups=True)
|
@strawberry_django.filter_type(models.OwnerGroup, lookups=True)
|
||||||
class OwnerGroupFilter(BaseModelFilter):
|
class OwnerGroupFilter(BaseModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% if customfield.type == 'integer' and value is not None %}
|
{% if customfield.type == 'integer' and value is not None %}
|
||||||
{{ value }}
|
{{ value }}
|
||||||
|
{% elif customfield.type == 'decimal' and value is not None %}
|
||||||
|
{{ value }}
|
||||||
{% elif customfield.type == 'longtext' and value %}
|
{% elif customfield.type == 'longtext' and value %}
|
||||||
{{ value|markdown }}
|
{{ value|markdown }}
|
||||||
{% elif customfield.type == 'boolean' and value == True %}
|
{% elif customfield.type == 'boolean' and value == True %}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import FilterLookup
|
from strawberry_django import StrFilterLookup
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .filters import VirtualMachineFilter
|
from .filters import VirtualMachineFilter
|
||||||
|
|
@ -20,5 +20,5 @@ class VMComponentFilterMixin:
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
virtual_machine_id: ID | None = strawberry_django.filter_field()
|
virtual_machine_id: ID | None = strawberry_django.filter_field()
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
description: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, FilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from dcim.graphql.filter_mixins import InterfaceBaseFilterMixin, RenderConfigFilterMixin, ScopedFilterMixin
|
from dcim.graphql.filter_mixins import InterfaceBaseFilterMixin, RenderConfigFilterMixin, ScopedFilterMixin
|
||||||
from extras.graphql.filter_mixins import ConfigContextFilterMixin
|
from extras.graphql.filter_mixins import ConfigContextFilterMixin
|
||||||
|
|
@ -39,7 +39,7 @@ __all__ = (
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Cluster, lookups=True)
|
@strawberry_django.filter_type(models.Cluster, lookups=True)
|
||||||
class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -77,7 +77,7 @@ class VirtualMachineFilter(
|
||||||
TenancyFilterMixin,
|
TenancyFilterMixin,
|
||||||
PrimaryModelFilter,
|
PrimaryModelFilter,
|
||||||
):
|
):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | 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()
|
site_id: ID | None = strawberry_django.filter_field()
|
||||||
cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
||||||
|
|
@ -116,7 +116,7 @@ class VirtualMachineFilter(
|
||||||
disk: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
disk: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
serial: FilterLookup[str] | None = strawberry_django.filter_field()
|
serial: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
interface_count: FilterLookup[int] | None = strawberry_django.filter_field()
|
interface_count: FilterLookup[int] | None = strawberry_django.filter_field()
|
||||||
virtual_disk_count: FilterLookup[int] | None = strawberry_django.filter_field()
|
virtual_disk_count: FilterLookup[int] | None = strawberry_django.filter_field()
|
||||||
interfaces: Annotated['VMInterfaceFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
interfaces: Annotated['VMInterfaceFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
||||||
from netbox.graphql.filters import (
|
from netbox.graphql.filters import (
|
||||||
|
|
@ -63,7 +63,7 @@ class TunnelTerminationFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLo
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.Tunnel, lookups=True)
|
@strawberry_django.filter_type(models.Tunnel, lookups=True)
|
||||||
class TunnelFilter(TenancyFilterMixin, PrimaryModelFilter):
|
class TunnelFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -89,7 +89,7 @@ class TunnelFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IKEProposal, lookups=True)
|
@strawberry_django.filter_type(models.IKEProposal, lookups=True)
|
||||||
class IKEProposalFilter(PrimaryModelFilter):
|
class IKEProposalFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
authentication_method: (
|
authentication_method: (
|
||||||
BaseFilterLookup[Annotated['AuthenticationMethodEnum', strawberry.lazy('vpn.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['AuthenticationMethodEnum', strawberry.lazy('vpn.graphql.enums')]] | None
|
||||||
) = (
|
) = (
|
||||||
|
|
@ -118,7 +118,7 @@ class IKEProposalFilter(PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IKEPolicy, lookups=True)
|
@strawberry_django.filter_type(models.IKEPolicy, lookups=True)
|
||||||
class IKEPolicyFilter(PrimaryModelFilter):
|
class IKEPolicyFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
version: BaseFilterLookup[Annotated['IKEVersionEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
version: BaseFilterLookup[Annotated['IKEVersionEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -128,12 +128,12 @@ class IKEPolicyFilter(PrimaryModelFilter):
|
||||||
proposals: Annotated['IKEProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = (
|
proposals: Annotated['IKEProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
preshared_key: FilterLookup[str] | None = strawberry_django.filter_field()
|
preshared_key: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IPSecProposal, lookups=True)
|
@strawberry_django.filter_type(models.IPSecProposal, lookups=True)
|
||||||
class IPSecProposalFilter(PrimaryModelFilter):
|
class IPSecProposalFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
encryption_algorithm: (
|
encryption_algorithm: (
|
||||||
BaseFilterLookup[Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')]] | None
|
BaseFilterLookup[Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')]] | None
|
||||||
) = (
|
) = (
|
||||||
|
|
@ -159,7 +159,7 @@ class IPSecProposalFilter(PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IPSecPolicy, lookups=True)
|
@strawberry_django.filter_type(models.IPSecPolicy, lookups=True)
|
||||||
class IPSecPolicyFilter(PrimaryModelFilter):
|
class IPSecPolicyFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
proposals: Annotated['IPSecProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = (
|
proposals: Annotated['IPSecProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -170,7 +170,7 @@ class IPSecPolicyFilter(PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.IPSecProfile, lookups=True)
|
@strawberry_django.filter_type(models.IPSecProfile, lookups=True)
|
||||||
class IPSecProfileFilter(PrimaryModelFilter):
|
class IPSecProfileFilter(PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
mode: BaseFilterLookup[Annotated['IPSecModeEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
mode: BaseFilterLookup[Annotated['IPSecModeEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -186,8 +186,8 @@ class IPSecProfileFilter(PrimaryModelFilter):
|
||||||
|
|
||||||
@strawberry_django.filter_type(models.L2VPN, lookups=True)
|
@strawberry_django.filter_type(models.L2VPN, lookups=True)
|
||||||
class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
name: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
slug: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
type: BaseFilterLookup[Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
type: BaseFilterLookup[Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
|
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry_django import FilterLookup
|
from strawberry_django import StrFilterLookup
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .enums import *
|
from .enums import *
|
||||||
|
|
@ -21,4 +21,4 @@ class WirelessAuthenticationFilterMixin:
|
||||||
auth_cipher: Annotated['WirelessAuthCipherEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
|
auth_cipher: Annotated['WirelessAuthCipherEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
auth_psk: FilterLookup[str] | None = strawberry_django.filter_field()
|
auth_psk: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||||
import strawberry
|
import strawberry
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry.scalars import ID
|
from strawberry.scalars import ID
|
||||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
from strawberry_django import BaseFilterLookup, StrFilterLookup
|
||||||
|
|
||||||
from dcim.graphql.filter_mixins import ScopedFilterMixin
|
from dcim.graphql.filter_mixins import ScopedFilterMixin
|
||||||
from netbox.graphql.filter_mixins import DistanceFilterMixin
|
from netbox.graphql.filter_mixins import DistanceFilterMixin
|
||||||
|
|
@ -38,7 +38,7 @@ class WirelessLANFilter(
|
||||||
TenancyFilterMixin,
|
TenancyFilterMixin,
|
||||||
PrimaryModelFilter
|
PrimaryModelFilter
|
||||||
):
|
):
|
||||||
ssid: FilterLookup[str] | None = strawberry_django.filter_field()
|
ssid: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
@ -65,7 +65,7 @@ class WirelessLinkFilter(
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
interface_b_id: ID | None = strawberry_django.filter_field()
|
interface_b_id: ID | None = strawberry_django.filter_field()
|
||||||
ssid: FilterLookup[str] | None = strawberry_django.filter_field()
|
ssid: StrFilterLookup[str] | None = strawberry_django.filter_field()
|
||||||
status: BaseFilterLookup[Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')]] | None = (
|
status: BaseFilterLookup[Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')]] | None = (
|
||||||
strawberry_django.filter_field()
|
strawberry_django.filter_field()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "netbox"
|
name = "netbox"
|
||||||
version = "4.5.3"
|
version = "4.5.4"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
description = "The premier source of truth powering network automation."
|
description = "The premier source of truth powering network automation."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
||||||
|
|
@ -17,27 +17,28 @@ django-taggit==6.1.0
|
||||||
django-timezone-field==7.2.1
|
django-timezone-field==7.2.1
|
||||||
djangorestframework==3.16.1
|
djangorestframework==3.16.1
|
||||||
drf-spectacular==0.29.0
|
drf-spectacular==0.29.0
|
||||||
drf-spectacular-sidecar==2026.1.1
|
drf-spectacular-sidecar==2026.3.1
|
||||||
feedparser==6.0.12
|
feedparser==6.0.12
|
||||||
gunicorn==25.0.3
|
gunicorn==25.1.0
|
||||||
Jinja2==3.1.6
|
Jinja2==3.1.6
|
||||||
jsonschema==4.26.0
|
jsonschema==4.26.0
|
||||||
Markdown==3.10.2
|
Markdown==3.10.2
|
||||||
mkdocs-material==9.7.1
|
mkdocs==1.6.1
|
||||||
|
mkdocs-material==9.7.3
|
||||||
mkdocstrings==1.0.3
|
mkdocstrings==1.0.3
|
||||||
mkdocstrings-python==2.0.2
|
mkdocstrings-python==2.0.3
|
||||||
netaddr==1.3.0
|
netaddr==1.3.0
|
||||||
nh3==0.3.3
|
nh3==0.3.3
|
||||||
Pillow==12.1.1
|
Pillow==12.1.1
|
||||||
psycopg[c,pool]==3.3.2
|
psycopg[c,pool]==3.3.3
|
||||||
PyYAML==6.0.3
|
PyYAML==6.0.3
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
rq==2.6.1
|
rq==2.7.0
|
||||||
social-auth-app-django==5.7.0
|
social-auth-app-django==5.7.0
|
||||||
social-auth-core==4.8.5
|
social-auth-core==4.8.5
|
||||||
sorl-thumbnail==13.0.0
|
sorl-thumbnail==13.0.0
|
||||||
strawberry-graphql==0.295.0
|
strawberry-graphql==0.307.1
|
||||||
strawberry-graphql-django==0.75.0
|
strawberry-graphql-django==0.79.0
|
||||||
svgwrite==1.4.3
|
svgwrite==1.4.3
|
||||||
tablib==3.9.0
|
tablib==3.9.0
|
||||||
tzdata==2025.3
|
tzdata==2025.3
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue