diff --git a/.github/ISSUE_TEMPLATE/01-feature_request.yaml b/.github/ISSUE_TEMPLATE/01-feature_request.yaml index 039e24fc4..a810b7a4c 100644 --- a/.github/ISSUE_TEMPLATE/01-feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/01-feature_request.yaml @@ -15,7 +15,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v4.2.9 + placeholder: v4.3.0 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/02-bug_report.yaml b/.github/ISSUE_TEMPLATE/02-bug_report.yaml index 0f18e6267..5194e7bc6 100644 --- a/.github/ISSUE_TEMPLATE/02-bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/02-bug_report.yaml @@ -27,7 +27,7 @@ body: attributes: label: NetBox Version description: What version of NetBox are you currently running? - placeholder: v4.2.9 + placeholder: v4.3.0 validations: required: true - type: dropdown diff --git a/base_requirements.txt b/base_requirements.txt index 9452cbd64..0c6e308e1 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -1,6 +1,6 @@ # The Python web framework on which NetBox is built # https://docs.djangoproject.com/en/stable/releases/ -Django<5.2 +Django==5.2.* # Django middleware which permits cross-domain API requests # https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst @@ -42,6 +42,10 @@ django-rich # https://github.com/rq/django-rq/blob/master/CHANGELOG.md django-rq +# Provides a variety of storage backends +# https://github.com/jschneier/django-storages/blob/master/CHANGELOG.rst +django-storages + # Abstraction models for rendering and paginating HTML tables # https://github.com/jieter/django-tables2/blob/master/CHANGELOG.md django-tables2 @@ -78,6 +82,10 @@ gunicorn # https://jinja.palletsprojects.com/changes/ Jinja2 +# JSON schema validation +# https://github.com/python-jsonschema/jsonschema/blob/main/CHANGELOG.rst +jsonschema + # Simple markup language for rendering HTML # https://python-markdown.github.io/changelog/ Markdown diff --git a/docs/administration/replicating-netbox.md b/docs/administration/replicating-netbox.md index 7cc4d3832..f702c3ffd 100644 --- a/docs/administration/replicating-netbox.md +++ b/docs/administration/replicating-netbox.md @@ -54,7 +54,7 @@ pg_dump --username netbox --password --host localhost -s netbox > netbox_schema. By default, NetBox stores uploaded files (such as image attachments) in its media directory. To fully replicate an instance of NetBox, you'll need to copy both the database and the media files. !!! note - These operations are not necessary if your installation is utilizing a [remote storage backend](../configuration/system.md#storage_backend). + These operations are not necessary if your installation is utilizing a [remote storage backend](../configuration/system.md#storages). ### Archive the Media Directory diff --git a/docs/configuration/plugins.md b/docs/configuration/plugins.md index 485e840e8..9e19622f9 100644 --- a/docs/configuration/plugins.md +++ b/docs/configuration/plugins.md @@ -33,3 +33,21 @@ Note that a plugin must be listed in `PLUGINS` for its configuration to take eff --- +## PLUGINS_CATALOG_CONFIG + +Default: Empty + +This parameter controls how individual plugins are displayed in the plugins catalog under Admin > System > Plugins. Adding a plugin to the `hidden` list will omit that plugin from the catalog. Adding a plugin to the `static` list will display the plugin, but not link to the plugin details or upgrade instructions. + +An example configuration is shown below: + +```python +PLUGINS_CATALOG_CONFIG = { + 'hidden': [ + 'plugin1', + ], + 'static': [ + 'plugin2', + ], +} +``` diff --git a/docs/configuration/required-parameters.md b/docs/configuration/required-parameters.md index f7e5d71ce..4a18e8a6c 100644 --- a/docs/configuration/required-parameters.md +++ b/docs/configuration/required-parameters.md @@ -25,7 +25,30 @@ ALLOWED_HOSTS = ['*'] ## DATABASE -NetBox requires access to a PostgreSQL 13 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary: +!!! warning "Legacy Configuration Parameter" + The `DATABASE` configuration parameter is deprecated and will be removed in a future release. Users are advised to adopt the new `DATABASES` (plural) parameter, which allows for the configuration of multiple databases. + +See the [`DATABASES`](#databases) configuration below for usage. + +--- + +## DATABASES + +!!! info "This parameter was introduced in NetBox v4.3." + +NetBox requires access to a PostgreSQL 14 or later database service to store data. This service can run locally on the NetBox server or on a remote system. Databases are defined as named dictionaries: + +```python +DATABASES = { + 'default': {...}, + 'external1': {...}, + 'external2': {...}, +} +``` + +NetBox itself requires only that a `default` database is defined. However, certain plugins may require the configuration of additional databases. (Consider also configuring the [`DATABASE_ROUTERS`](./system.md#database_routers) parameter when multiple databases are in use.) + +The following parameters must be defined for each database: * `NAME` - Database name * `USER` - PostgreSQL username @@ -38,14 +61,16 @@ NetBox requires access to a PostgreSQL 13 or later database service to store dat Example: ```python -DATABASE = { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'netbox', # Database name - 'USER': 'netbox', # PostgreSQL username - 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password - 'HOST': 'localhost', # Database server - 'PORT': '', # Database port (leave blank for default) - 'CONN_MAX_AGE': 300, # Max database connection age +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'netbox', # Database name + 'USER': 'netbox', # PostgreSQL username + 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password + 'HOST': 'localhost', # Database server + 'PORT': '', # Database port (leave blank for default) + 'CONN_MAX_AGE': 300, # Max database connection age + } } ``` @@ -53,7 +78,7 @@ DATABASE = { NetBox supports all PostgreSQL database options supported by the underlying Django framework. For a complete list of available parameters, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/settings/#databases). !!! warning - Make sure to use a PostgreSQL-compatible backend for the ENGINE setting. If you don't specify an ENGINE, the default will be django.db.backends.postgresql. + The `ENGINE` parameter must specify a PostgreSQL-compatible database backend. If not defined, the default engine `django.db.backends.postgresql` will be used. --- diff --git a/docs/configuration/security.md b/docs/configuration/security.md index 7f539bc96..771eba5c5 100644 --- a/docs/configuration/security.md +++ b/docs/configuration/security.md @@ -2,7 +2,10 @@ ## ALLOW_TOKEN_RETRIEVAL -Default: `True` +Default: `False` + +!!! note + The default value of this parameter changed from true to false in NetBox v4.3.0. If disabled, the values of API tokens will not be displayed after each token's initial creation. A user **must** record the value of a token prior to its creation, or it will be lost. Note that this affects _all_ users, regardless of assigned permissions. @@ -186,6 +189,17 @@ The lifetime (in seconds) of the authentication cookie issued to a NetBox user u --- +## LOGIN_FORM_HIDDEN + +Default: False + +Option to hide the login form when only SSO authentication is in use. + +!!! warning + If the SSO provider is unreachable, login to NetBox will be impossible if this option is enabled. The only recourse is to disable it in the local configuration and restart the NetBox service. + +--- + ## LOGOUT_REDIRECT_URL Default: `'home'` diff --git a/docs/configuration/system.md b/docs/configuration/system.md index 1414cff85..fe01e40b1 100644 --- a/docs/configuration/system.md +++ b/docs/configuration/system.md @@ -12,6 +12,16 @@ BASE_PATH = 'netbox/' --- +## DATABASE_ROUTERS + +!!! info "This parameter was introduced in NetBox v4.3." + +Default: `[]` (empty list) + +An iterable of [database routers](https://docs.djangoproject.com/en/stable/topics/db/multi-db/) to use for automatically selecting the appropriate database(s) for a query. This is useful only when [multiple databases](./required-parameters.md#databases) have been configured. + +--- + ## DEFAULT_LANGUAGE Default: `en-us` (US English) @@ -75,6 +85,8 @@ HTTP_PROXIES = { } ``` +If more flexibility is needed in determining which proxy to use for a given request, consider implementing one or more custom proxy routers via the [`PROXY_ROUTERS`](#proxy_routers) parameter. + --- ## INTERNAL_IPS @@ -160,6 +172,18 @@ The file path to the location where media files (such as image attachments) are --- +## PROXY_ROUTERS + +!!! info "This parameter was introduced in NetBox v4.3." + +Default: `["utilities.proxy.DefaultProxyRouter"]` + +A list of Python classes responsible for determining which proxy server(s) to use for outbound HTTP requests. Each item in the list can be the class itself or the dotted path to the class. + +The `route()` method on each class must return a dictionary of candidate proxies arranged by protocol (e.g. `http` and/or `https`), or None if no viable proxy can be determined. The default class, `DefaultProxyRouter`, simply returns the content of [`HTTP_PROXIES`](#http_proxies). + +--- + ## REPORTS_ROOT Default: `$INSTALL_ROOT/netbox/reports/` @@ -184,23 +208,46 @@ The dotted path to the desired search backend class. `CachedValueSearchBackend` --- -## STORAGE_BACKEND +## STORAGES -Default: None (local storage) +The backend storage engine for handling uploaded files such as [image attachments](../models/extras/imageattachment.md) and [custom scripts](../customization/custom-scripts.md). NetBox integrates with the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) and [`django-storage-swift`](https://github.com/dennisv/django-storage-swift) libraries, which provide backends for several popular file storage services. If not configured, local filesystem storage will be used. -The backend storage engine for handling uploaded files (e.g. image attachments). NetBox supports integration with the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) and [`django-storage-swift`](https://github.com/dennisv/django-storage-swift) packages, which provide backends for several popular file storage services. If not configured, local filesystem storage will be used. +By default, the following configuration is used: -The configuration parameters for the specified storage backend are defined under the `STORAGE_CONFIG` setting. +```python +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + "scripts": { + "BACKEND": "extras.storage.ScriptFileSystemStorage", + }, +} +``` ---- +Within the `STORAGES` dictionary, `"default"` is used for image uploads, "staticfiles" is for static files and `"scripts"` is used for custom scripts. -## STORAGE_CONFIG +If using a remote storage like S3, define the config as `STORAGES[key]["OPTIONS"]` for each storage item as needed. For example: -Default: Empty +```python +STORAGES = { + "scripts": { + "BACKEND": "storages.backends.s3boto3.S3Boto3Storage", + "OPTIONS": { + 'access_key': 'access key', + 'secret_key': 'secret key', + } + }, +} +``` -A dictionary of configuration parameters for the storage backend configured as `STORAGE_BACKEND`. The specific parameters to be used here are specific to each backend; see the documentation for your selected backend ([`django-storages`](https://django-storages.readthedocs.io/en/stable/) or [`django-storage-swift`](https://github.com/dennisv/django-storage-swift)) for more detail. +The specific configuration settings for each storage backend can be found in the [django-storages documentation](https://django-storages.readthedocs.io/en/latest/index.html). -If `STORAGE_BACKEND` is not defined, this setting will be ignored. +!!! note + Any keys defined in the `STORAGES` configuration parameter replace those in the default configuration. It is only necessary to define keys within the `STORAGES` for the specific backend(s) you wish to configure. --- diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md index 7774ef35f..e7536a654 100644 --- a/docs/customization/custom-scripts.md +++ b/docs/customization/custom-scripts.md @@ -140,6 +140,8 @@ The Script class provides two convenience methods for reading data from files: These two methods will load data in YAML or JSON format, respectively, from files within the local path (i.e. `SCRIPTS_ROOT`). +**Note:** These convenience methods are deprecated and will be removed in NetBox v4.4. These only work if running scripts within the local path, they will not work if using a storage other than ScriptFileSystemStorage. + ## Logging The Script object provides a set of convenient functions for recording messages at different severity levels: diff --git a/docs/development/adding-models.md b/docs/development/adding-models.md index 0bf020662..da0e49511 100644 --- a/docs/development/adding-models.md +++ b/docs/development/adding-models.md @@ -76,11 +76,13 @@ Create the following for each model: ## 13. GraphQL API components -Create a GraphQL object type for the model in `graphql/types.py` by subclassing the appropriate class from `netbox.graphql.types`. +Create the following for each model: -**Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["policy"])` or similar. +* GraphQL object type for the model in `graphql/types.py` (subclass the appropriate class from `netbox.graphql.types`) +* Add a GraphQL filter for the model in `graphql/filters.py` +* Extend the query class for the app in `graphql/schema.py` with the individual object and object list fields -Also extend the schema class defined in `graphql/schema.py` with the individual object and object list fields per the established convention. +**Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["foo"])` or similar. ## 14. Add tests diff --git a/docs/development/extending-models.md b/docs/development/extending-models.md index 16d1c3451..d870a371d 100644 --- a/docs/development/extending-models.md +++ b/docs/development/extending-models.md @@ -6,7 +6,7 @@ Below is a list of tasks to consider when adding a new field to a core model. Add the field to the model, taking care to address any of the following conditions. -* When adding a GenericForeignKey field, also add an index under `Meta` for its two concrete fields. For example: +* When adding a GenericForeignKey field, you may need add an index under `Meta` for its two concrete fields. (This is required only for non-unique GFK relationships, as the unique constraint introduces its own index.) For example: ```python class Meta: diff --git a/docs/development/getting-started.md b/docs/development/getting-started.md index 0b77bfd4d..129bf2d4b 100644 --- a/docs/development/getting-started.md +++ b/docs/development/getting-started.md @@ -115,7 +115,7 @@ You may also need to set up the yarn packages as shown in the [Web UI Developmen Within the `netbox/netbox/` directory, copy `configuration_example.py` to `configuration.py` and update the following parameters: * `ALLOWED_HOSTS`: This can be set to `['*']` for development purposes -* `DATABASE`: PostgreSQL database connection parameters +* `DATABASES`: PostgreSQL database connection parameters * `REDIS`: Redis configuration (if different from the defaults) * `SECRET_KEY`: Set to a random string (use `generate_secret_key.py` in the parent directory to generate a suitable key) * `DEBUG`: Set to `True` diff --git a/docs/installation/1-postgresql.md b/docs/installation/1-postgresql.md index 8ba302909..0d74eea05 100644 --- a/docs/installation/1-postgresql.md +++ b/docs/installation/1-postgresql.md @@ -2,39 +2,17 @@ This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md). -!!! warning "PostgreSQL 13 or later required" - NetBox requires PostgreSQL 13 or later. Please note that MySQL and other relational databases are **not** supported. +!!! warning "PostgreSQL 14 or later required" + NetBox requires PostgreSQL 14 or later. Please note that MySQL and other relational databases are **not** supported. ## Installation -=== "Ubuntu" +```no-highlight +sudo apt update +sudo apt install -y postgresql +``` - ```no-highlight - sudo apt update - sudo apt install -y postgresql - ``` - -=== "CentOS" - - ```no-highlight - sudo yum install -y postgresql-server - sudo postgresql-setup --initdb - ``` - - CentOS configures ident host-based authentication for PostgreSQL by default. Because NetBox will need to authenticate using a username and password, modify `/var/lib/pgsql/data/pg_hba.conf` to support MD5 authentication by changing `ident` to `md5` for the lines below: - - ```no-highlight - host all all 127.0.0.1/32 md5 - host all all ::1/128 md5 - ``` - - Once PostgreSQL has been installed, start the service and enable it to run at boot: - - ```no-highlight - sudo systemctl enable --now postgresql - ``` - -Before continuing, verify that you have installed PostgreSQL 13 or later: +Before continuing, verify that you have installed PostgreSQL 14 or later: ```no-highlight psql -V diff --git a/docs/installation/2-redis.md b/docs/installation/2-redis.md index 2756a1ab0..c29deb5c2 100644 --- a/docs/installation/2-redis.md +++ b/docs/installation/2-redis.md @@ -4,18 +4,9 @@ [Redis](https://redis.io/) is an in-memory key-value store which NetBox employs for caching and queuing. This section entails the installation and configuration of a local Redis instance. If you already have a Redis service in place, skip to [the next section](3-netbox.md). -=== "Ubuntu" - - ```no-highlight - sudo apt install -y redis-server - ``` - -=== "CentOS" - - ```no-highlight - sudo yum install -y redis - sudo systemctl enable --now redis - ``` +```no-highlight +sudo apt install -y redis-server +``` Before continuing, verify that your installed version of Redis is at least v4.0: diff --git a/docs/installation/3-netbox.md b/docs/installation/3-netbox.md index 0a5f51702..67a19e2e3 100644 --- a/docs/installation/3-netbox.md +++ b/docs/installation/3-netbox.md @@ -9,17 +9,11 @@ Begin by installing all system packages required by NetBox and its dependencies. !!! warning "Python 3.10 or later required" NetBox supports Python 3.10, 3.11, and 3.12. -=== "Ubuntu" - - ```no-highlight - sudo apt install -y python3 python3-pip python3-venv python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev - ``` - -=== "CentOS" - - ```no-highlight - sudo yum install -y gcc libxml2-devel libxslt-devel libffi-devel libpq-devel openssl-devel redhat-rpm-config - ``` +```no-highlight +sudo apt install -y python3 python3-pip python3-venv python3-dev \ +build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev \ +libssl-dev zlib1g-dev +``` Before continuing, check that your installed Python version is at least 3.10: @@ -55,17 +49,9 @@ cd /opt/netbox/ If `git` is not already installed, install it: -=== "Ubuntu" - - ```no-highlight - sudo apt install -y git - ``` - -=== "CentOS" - - ```no-highlight - sudo yum install -y git - ``` +```no-highlight +sudo apt install -y git +``` Next, clone the git repository: @@ -97,24 +83,12 @@ Using this installation method enables easy upgrades in the future by simply che Create a system user account named `netbox`. We'll configure the WSGI and HTTP services to run under this account. We'll also assign this user ownership of the media directory. This ensures that NetBox will be able to save uploaded files. -=== "Ubuntu" - - ``` - sudo adduser --system --group netbox - sudo chown --recursive netbox /opt/netbox/netbox/media/ - sudo chown --recursive netbox /opt/netbox/netbox/reports/ - sudo chown --recursive netbox /opt/netbox/netbox/scripts/ - ``` - -=== "CentOS" - - ``` - sudo groupadd --system netbox - sudo adduser --system -g netbox netbox - sudo chown --recursive netbox /opt/netbox/netbox/media/ - sudo chown --recursive netbox /opt/netbox/netbox/reports/ - sudo chown --recursive netbox /opt/netbox/netbox/scripts/ - ``` +``` +sudo adduser --system --group netbox +sudo chown --recursive netbox /opt/netbox/netbox/media/ +sudo chown --recursive netbox /opt/netbox/netbox/reports/ +sudo chown --recursive netbox /opt/netbox/netbox/scripts/ +``` ## Configuration @@ -128,7 +102,7 @@ sudo cp configuration_example.py configuration.py Open `configuration.py` with your preferred editor to begin configuring NetBox. NetBox offers [many configuration parameters](../configuration/index.md), but only the following four are required for new installations: * `ALLOWED_HOSTS` -* `DATABASE` +* `DATABASES` (or `DATABASE`) * `REDIS` * `SECRET_KEY` @@ -146,18 +120,22 @@ If you are not yet sure what the domain name and/or IP address of the NetBox ins ALLOWED_HOSTS = ['*'] ``` -### DATABASE +### DATABASES -This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, update the `HOST` and `PORT` parameters accordingly. See the [configuration documentation](../configuration/required-parameters.md#database) for more detail on individual parameters. +This parameter holds the PostgreSQL database configuration details. The default database must be defined; additional databases may be defined as needed e.g. by plugins. + +A username and password must be defined for the default database. If the service is running on a remote host, update the `HOST` and `PORT` parameters accordingly. See the [configuration documentation](../configuration/required-parameters.md#databases) for more detail on individual parameters. ```python -DATABASE = { - 'NAME': 'netbox', # Database name - 'USER': 'netbox', # PostgreSQL username - 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password - 'HOST': 'localhost', # Database server - 'PORT': '', # Database port (leave blank for default) - 'CONN_MAX_AGE': 300, # Max database connection age (seconds) +DATABASES = { + 'default': { + 'NAME': 'netbox', # Database name + 'USER': 'netbox', # PostgreSQL username + 'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password + 'HOST': 'localhost', # Database server + 'PORT': '', # Database port (leave blank for default) + 'CONN_MAX_AGE': 300, # Max database connection age (seconds) + } } ``` @@ -207,7 +185,7 @@ All Python packages required by NetBox are listed in `requirements.txt` and will ### Remote File Storage -By default, NetBox will use the local filesystem to store uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired storage backend](../configuration/system.md#storage_backend) in `configuration.py`. +By default, NetBox will use the local filesystem to store uploaded files. To use a remote filesystem, install the [`django-storages`](https://django-storages.readthedocs.io/en/stable/) library and configure your [desired storage backend](../configuration/system.md#storages) in `configuration.py`. ```no-highlight sudo sh -c "echo 'django-storages' >> /opt/netbox/local_requirements.txt" diff --git a/docs/installation/6-ldap.md b/docs/installation/6-ldap.md index 6ee1c9901..7de9f116d 100644 --- a/docs/installation/6-ldap.md +++ b/docs/installation/6-ldap.md @@ -6,18 +6,10 @@ This guide explains how to implement LDAP authentication using an external serve ### Install System Packages -On Ubuntu: - ```no-highlight sudo apt install -y libldap2-dev libsasl2-dev libssl-dev ``` -On CentOS: - -```no-highlight -sudo yum install -y openldap-devel python3-devel -``` - ### Install django-auth-ldap Activate the Python virtual environment and install the `django-auth-ldap` package using pip: diff --git a/docs/installation/index.md b/docs/installation/index.md index 877b177eb..aefa39d17 100644 --- a/docs/installation/index.md +++ b/docs/installation/index.md @@ -1,9 +1,18 @@ # Installation -!!! info "NetBox Cloud" - The instructions below are for installing NetBox as a standalone, self-hosted application. For a Cloud-delivered solution, check out [NetBox Cloud](https://netboxlabs.com/netbox-cloud/) by NetBox Labs. +
-The installation instructions provided here have been tested to work on Ubuntu 22.04 and CentOS 8.3. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors. +- :material-clock-fast:{ .lg .middle } __Eager to Get Started?__ + + --- + + Check out the [NetBox Cloud Free Plan](https://netboxlabs.com/free-netbox-cloud/)! Skip the installation process and grab your own NetBox Cloud instance, preconfigured and ready to go in minutes. Completely free! + + [:octicons-arrow-right-24: Sign Up](https://signup.netboxlabs.com/) + +
+ +The installation instructions provided here have been tested to work on Ubuntu 22.04. The particular commands needed to install dependencies on other distributions may vary significantly. Unfortunately, this is outside the control of the NetBox maintainers. Please consult your distribution's documentation for assistance with any errors. The following sections detail how to set up a new instance of NetBox: @@ -19,7 +28,7 @@ The following sections detail how to set up a new instance of NetBox: | Dependency | Supported Versions | |------------|--------------------| | Python | 3.10, 3.11, 3.12 | -| PostgreSQL | 13+ | +| PostgreSQL | 14+ | | Redis | 4.0+ | Below is a simplified overview of the NetBox application stack for reference: diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 42b8c0187..f9a7a3189 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -17,52 +17,50 @@ Prior to upgrading your NetBox instance, be sure to carefully review all [releas NetBox requires the following dependencies: -=== "Current Version" +| Dependency | Supported Versions | +|------------|--------------------| +| Python | 3.10, 3.11, 3.12 | +| PostgreSQL | 14+ | +| Redis | 4.0+ | - | Dependency | Supported Versions | - |------------|--------------------| - | Python | 3.10, 3.11, 3.12 | - | PostgreSQL | 13+ | - | Redis | 4.0+ | - -=== "All Versions" - - | NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation | - |:--------------:|:----------:|:----------:|:--------------:|:---------:|:-------------------------------------------------------------------------------------------------:| - | 4.2 | 3.10 | 3.12 | **13** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.2.0/docs/installation/index.md) | - | 4.1 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.1.0/docs/installation/index.md) | - | 4.0 | **3.10** | **3.12** | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.0.0/docs/installation/index.md) | - | 3.7 | 3.8 | 3.11 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.7.0/docs/installation/index.md) | - | 3.6 | 3.8 | **3.11** | **12** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.6.0/docs/installation/index.md) | - | 3.5 | 3.8 | 3.10 | 11 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.5.0/docs/installation/index.md) | - | 3.4 | 3.8 | 3.10 | **11** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.4.0/docs/installation/index.md) | - | 3.3 | 3.8 | 3.10 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.3.0/docs/installation/index.md) | - | 3.2 | **3.8** | **3.10** | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.2.0/docs/installation/index.md) | - | 3.1 | 3.7 | 3.9 | **10** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.1.0/docs/installation/index.md) | - | 3.0 | **3.7** | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.0.0/docs/installation/index.md) | - | 2.11 | 3.6 | **3.9** | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.11.0/docs/installation/index.md) | - | 2.10 | 3.6 | 3.8 | **9.6** | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.10.0/docs/installation/index.md) | - | 2.9 | 3.6 | 3.8 | 9.5 | **4.0** | [Link](https://github.com/netbox-community/netbox/blob/v2.9.0/docs/installation/index.md) | - | 2.8 | **3.6** | **3.8** | **9.5** | **3.4** | [Link](https://github.com/netbox-community/netbox/blob/v2.8.0/docs/installation/index.md) | - | 2.7 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.7.0/docs/installation/index.md) | - | 2.6 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.6.0/docs/installation/index.md) | - | 2.5 | **3.5** | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.5.0/docs/installation/index.md) | - | 2.4 | **3.4** | **3.7** | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.4.0/docs/installation/index.md) | - | 2.3 | 2.7 | 3.6 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.3.0/docs/installation/postgresql.md) | - | 2.2 | 2.7 | 3.6 | **9.4** | - | [Link](https://github.com/netbox-community/netbox/blob/v2.2.0/docs/installation/postgresql.md) | - | 2.1 | 2.7 | 3.6 | 9.3 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.1.0/docs/installation/postgresql.md) | - | 2.0 | 2.7 | **3.6** | **9.3** | - | [Link](https://github.com/netbox-community/netbox/blob/v2.0.0/docs/installation/postgresql.md) | - | 1.9 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.9.0-r1/docs/installation/postgresql.md) | - | 1.8 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.8.0/docs/installation/postgresql.md) | - | 1.7 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.7.0/docs/installation/postgresql.md) | - | 1.6 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.6.0/docs/installation/postgresql.md) | - | 1.5 | 2.7 | 3.5 | **9.2** | - | [Link](https://github.com/netbox-community/netbox/blob/v1.5.0/docs/installation/postgresql.md) | - | 1.4 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.4.0/docs/installation/postgresql.md) | - | 1.3 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.3.0/docs/installation/postgresql.md) | - | 1.2 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.2.0/docs/installation/postgresql.md) | - | 1.1 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.1.0/docs/getting-started.md) | - | 1.0 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/1.0.0/docs/getting-started.md) | +### Version History +| NetBox Version | Python min | Python max | PostgreSQL min | Redis min | Documentation | +|:--------------:|:----------:|:----------:|:--------------:|:---------:|:-------------------------------------------------------------------------------------------------:| +| 4.3 | 3.10 | 3.12 | 14 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.3.0/docs/installation/index.md) | +| 4.2 | 3.10 | 3.12 | 13 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.2.0/docs/installation/index.md) | +| 4.1 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.1.0/docs/installation/index.md) | +| 4.0 | 3.10 | 3.12 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v4.0.0/docs/installation/index.md) | +| 3.7 | 3.8 | 3.11 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.7.0/docs/installation/index.md) | +| 3.6 | 3.8 | 3.11 | 12 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.6.0/docs/installation/index.md) | +| 3.5 | 3.8 | 3.10 | 11 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.5.0/docs/installation/index.md) | +| 3.4 | 3.8 | 3.10 | 11 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.4.0/docs/installation/index.md) | +| 3.3 | 3.8 | 3.10 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.3.0/docs/installation/index.md) | +| 3.2 | 3.8 | 3.10 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.2.0/docs/installation/index.md) | +| 3.1 | 3.7 | 3.9 | 10 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.1.0/docs/installation/index.md) | +| 3.0 | 3.7 | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v3.0.0/docs/installation/index.md) | +| 2.11 | 3.6 | 3.9 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.11.0/docs/installation/index.md) | +| 2.10 | 3.6 | 3.8 | 9.6 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.10.0/docs/installation/index.md) | +| 2.9 | 3.6 | 3.8 | 9.5 | 4.0 | [Link](https://github.com/netbox-community/netbox/blob/v2.9.0/docs/installation/index.md) | +| 2.8 | 3.6 | 3.8 | 9.5 | 3.4 | [Link](https://github.com/netbox-community/netbox/blob/v2.8.0/docs/installation/index.md) | +| 2.7 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.7.0/docs/installation/index.md) | +| 2.6 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.6.0/docs/installation/index.md) | +| 2.5 | 3.5 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.5.0/docs/installation/index.md) | +| 2.4 | 3.4 | 3.7 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.4.0/docs/installation/index.md) | +| 2.3 | 2.7 | 3.6 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.3.0/docs/installation/postgresql.md) | +| 2.2 | 2.7 | 3.6 | 9.4 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.2.0/docs/installation/postgresql.md) | +| 2.1 | 2.7 | 3.6 | 9.3 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.1.0/docs/installation/postgresql.md) | +| 2.0 | 2.7 | 3.6 | 9.3 | - | [Link](https://github.com/netbox-community/netbox/blob/v2.0.0/docs/installation/postgresql.md) | +| 1.9 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.9.0-r1/docs/installation/postgresql.md) | +| 1.8 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.8.0/docs/installation/postgresql.md) | +| 1.7 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.7.0/docs/installation/postgresql.md) | +| 1.6 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.6.0/docs/installation/postgresql.md) | +| 1.5 | 2.7 | 3.5 | 9.2 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.5.0/docs/installation/postgresql.md) | +| 1.4 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.4.0/docs/installation/postgresql.md) | +| 1.3 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.3.0/docs/installation/postgresql.md) | +| 1.2 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.2.0/docs/installation/postgresql.md) | +| 1.1 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/v1.1.0/docs/getting-started.md) | +| 1.0 | 2.7 | 3.5 | 9.1 | - | [Link](https://github.com/netbox-community/netbox/blob/1.0.0/docs/getting-started.md) | ## 3. Install the Latest Release diff --git a/docs/integrations/graphql-api.md b/docs/integrations/graphql-api.md index 87d9d8c49..39309671c 100644 --- a/docs/integrations/graphql-api.md +++ b/docs/integrations/graphql-api.md @@ -11,7 +11,7 @@ curl -H "Authorization: Token $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ http://netbox/graphql/ \ ---data '{"query": "query {circuit_list(status:\"active\") {cid provider {name}}}"}' +--data '{"query": "query {circuit_list(filters:{status: STATUS_ACTIVE}) {cid provider {name}}}"}' ``` The response will include the requested data formatted as JSON: @@ -51,20 +51,48 @@ For more detail on constructing GraphQL queries, see the [GraphQL queries docume ## Filtering -The GraphQL API employs the same filtering logic as the UI and REST API. Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only sites within the North Carolina region with a status of active: +!!! note "Changed in NetBox v4.3" + The filtering syntax fo the GraphQL API has changed substantially in NetBox v4.3. + +Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only active sites: ``` query { - site_list(filters: {region: "us-nc", status: "active"}) { + site_list( + filters: { + status: STATUS_ACTIVE + } + ) { name } } ``` -In addition, filtering can be done on list of related objects as shown in the following query: +Filters can be combined with logical operators, such as `OR` and `NOT`. For example, the following will return every site that is planned _or_ assigned to a tenant named Foo: ``` -{ +query { + site_list( + filters: { + status: STATUS_PLANNED, + OR: { + tenant: { + name: { + exact: "Foo" + } + } + } + } + ) { + name + } +} +``` + +Filtering can also be applied to related objects. For example, the following query will return only enabled interfaces for each device: + +``` +query { device_list { id name @@ -103,6 +131,18 @@ Certain queries can return multiple types of objects, for example cable terminat The field "class_type" is an easy way to distinguish what type of object it is when viewing the returned data, or when filtering. It contains the class name, for example "CircuitTermination" or "ConsoleServerPort". +## Pagination + +Queries can be paginated by specifying pagination in the query and supplying an offset and optionaly a limit in the query. If no limit is given, a default of 100 is used. Queries are not paginated unless requested in the query. An example paginated query is shown below: + +``` +query { + device_list(pagination: { offset: 0, limit: 20 }) { + id + } +} +``` + ## Authentication NetBox's GraphQL API uses the same API authentication tokens as its REST API. Authentication tokens are included with requests by attaching an `Authorization` HTTP header in the following form: diff --git a/docs/introduction.md b/docs/introduction.md index 75701c119..c8e5ee8ac 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -79,5 +79,5 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and | HTTP service | nginx or Apache | | WSGI service | gunicorn or uWSGI | | Application | Django/Python | -| Database | PostgreSQL 13+ | +| Database | PostgreSQL 14+ | | Task queuing | Redis/django-rq | diff --git a/docs/models/core/datasource.md b/docs/models/core/datasource.md index 0e18a2aae..527d93939 100644 --- a/docs/models/core/datasource.md +++ b/docs/models/core/datasource.md @@ -44,6 +44,12 @@ A set of rules (one per line) identifying filenames to ignore during synchroniza | `*.txt` | Ignore any files with a `.txt` extension | | `data???.json` | Ignore e.g. `data123.json` | +### Sync Interval + +!!! info "This field was introduced in NetBox v4.3." + +The interval at which the data source should automatically synchronize. If not set, the data source must be synchronized manually. + ### Last Synced The date and time at which the source was most recently synchronized successfully. diff --git a/docs/models/dcim/devicerole.md b/docs/models/dcim/devicerole.md index 786170f2b..abff149d6 100644 --- a/docs/models/dcim/devicerole.md +++ b/docs/models/dcim/devicerole.md @@ -4,6 +4,12 @@ Devices can be organized by functional roles, which are fully customizable by th ## Fields +### Parent + +!!! info "This field was introduced in NetBox v4.3." + +The parent role of which this role is a child (optional). + ### Name A unique human-friendly name. diff --git a/docs/models/dcim/inventoryitem.md b/docs/models/dcim/inventoryitem.md index 2d648341b..6aed0fc86 100644 --- a/docs/models/dcim/inventoryitem.md +++ b/docs/models/dcim/inventoryitem.md @@ -1,5 +1,8 @@ # Inventory Items +!!! warning "Deprecation Warning" + Beginning in NetBox v4.3, the use of inventory items has been deprecated. They are planned for removal in a future NetBox release. Users are strongly encouraged to begin using [modules](./module.md) and [module types](./moduletype.md) in place of inventory items. Modules provide enhanced functionality and can be configured with user-defined attributes. + Inventory items represent hardware components installed within a device, such as a power supply or CPU or line card. They are intended to be used primarily for inventory purposes. Inventory items are hierarchical in nature, such that any individual item may be designated as the parent for other items. For example, an inventory item might be created to represent a line card which houses several SFP optics, each of which exists as a child item within the device. An inventory item may also be associated with a specific component within the same device. For example, you may wish to associate a transceiver with an interface. diff --git a/docs/models/dcim/inventoryitemrole.md b/docs/models/dcim/inventoryitemrole.md index 50eb61abd..b77637604 100644 --- a/docs/models/dcim/inventoryitemrole.md +++ b/docs/models/dcim/inventoryitemrole.md @@ -1,5 +1,8 @@ # Inventory Item Roles +!!! warning "Deprecation Warning" + Beginning in NetBox v4.3, the use of inventory items has been deprecated. They are planned for removal in a future NetBox release. Users are strongly encouraged to begin using [modules](./module.md) and [module types](./moduletype.md) in place of inventory items. Modules provide enhanced functionality and can be configured with user-defined attributes. + Inventory items can be organized by functional roles, which are fully customizable by the user. For example, you might create roles for power supplies, fans, interface optics, etc. ## Fields diff --git a/docs/models/dcim/inventoryitemtemplate.md b/docs/models/dcim/inventoryitemtemplate.md index 02fde5995..7d8ff504d 100644 --- a/docs/models/dcim/inventoryitemtemplate.md +++ b/docs/models/dcim/inventoryitemtemplate.md @@ -1,3 +1,6 @@ # Inventory Item Templates +!!! warning "Deprecation Warning" + Beginning in NetBox v4.3, the use of inventory items has been deprecated. They are planned for removal in a future NetBox release. Users are strongly encouraged to begin using [modules](./module.md) and [module types](./moduletype.md) in place of inventory items. Modules provide enhanced functionality and can be configured with user-defined attributes. + A template for an inventory item that will be automatically created when instantiating a new device. All attributes of this object will be copied to the new inventory item, including the associations with a parent item and assigned component, if any. See the [inventory item](./inventoryitem.md) documentation for more detail. diff --git a/docs/models/dcim/moduletype.md b/docs/models/dcim/moduletype.md index 7077e16c2..88f04466a 100644 --- a/docs/models/dcim/moduletype.md +++ b/docs/models/dcim/moduletype.md @@ -43,3 +43,11 @@ The numeric weight of the module, including a unit designation (e.g. 3 kilograms ### Airflow The direction in which air circulates through the device chassis for cooling. + +### Profile + +The assigned [profile](./moduletypeprofile.md) for the type of module. Profiles can be used to classify module types by function (e.g. power supply, hard disk, etc.), and they support the addition of user-configurable attributes on module types. The assignment of a module type to a profile is optional. + +### Attributes + +Depending on the module type's assigned [profile](./moduletypeprofile.md) (if any), one or more user-defined attributes may be available to configure. diff --git a/docs/models/dcim/moduletypeprofile.md b/docs/models/dcim/moduletypeprofile.md new file mode 100644 index 000000000..80345c82b --- /dev/null +++ b/docs/models/dcim/moduletypeprofile.md @@ -0,0 +1,40 @@ +# Module Type Profiles + +!!! info "This model was introduced in NetBox v4.3." + +Each [module type](./moduletype.md) may optionally be assigned a profile according to its classification. A profile can extend module types with user-configured attributes. For example, you might want to specify the input current and voltage of a power supply, or the clock speed and number of cores for a processor. + +Module type attributes are managed via the configuration of a [JSON schema](https://json-schema.org/) on the profile. For example, the following schema introduces three module type attributes, two of which are designated as required attributes. + +```json +{ + "properties": { + "type": { + "type": "string", + "title": "Disk type", + "enum": ["HD", "SSD", "NVME"], + "default": "HD" + }, + "capacity": { + "type": "integer", + "title": "Capacity (GB)", + "description": "Gross disk size" + }, + "speed": { + "type": "integer", + "title": "Speed (RPM)" + } + }, + "required": [ + "type", "capacity" + ] +} +``` + +The assignment of module types to a profile is optional. The designation of a schema for a profile is also optional: A profile can be used simply as a mechanism for classifying module types if the addition of custom attributes is not needed. + +## Fields + +### Schema + +This field holds the [JSON schema](https://json-schema.org/) for the profile. The configured JSON schema must be valid (or the field must be null). diff --git a/docs/models/dcim/poweroutlet.md b/docs/models/dcim/poweroutlet.md index a99f60b23..22a7ec63e 100644 --- a/docs/models/dcim/poweroutlet.md +++ b/docs/models/dcim/poweroutlet.md @@ -29,6 +29,19 @@ An alternative physical label identifying the power outlet. The type of power outlet. +### Status + +The operational status of the power outlet. By default, the following statuses are available: + +* Enabled +* Disabled +* Faulty + +!!! tip "Custom power outlet statuses" + Additional power outlet statuses may be defined by setting `PowerOutlet.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. + +!!! info "This field was introduced in NetBox v4.3." + ### Color !!! info "This field was introduced in NetBox v4.2." diff --git a/docs/models/dcim/racktype.md b/docs/models/dcim/racktype.md index b5f2d99e7..5298e8b26 100644 --- a/docs/models/dcim/racktype.md +++ b/docs/models/dcim/racktype.md @@ -40,7 +40,9 @@ The number of the numerically lowest unit in the rack. This value defaults to on ### Outer Dimensions -The external width and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches. +The external width, height and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches. + +!!! info "The `outer_height` field was introduced in NetBox v4.3." ### Mounting Depth diff --git a/docs/models/extras/branch.md b/docs/models/extras/branch.md deleted file mode 100644 index 4599fed85..000000000 --- a/docs/models/extras/branch.md +++ /dev/null @@ -1,16 +0,0 @@ -# Branches - -!!! danger "Deprecated Feature" - This feature has been deprecated in NetBox v4.2 and will be removed in a future release. Please consider using the [netbox-branching plugin](https://github.com/netboxlabs/netbox-branching), which provides much more robust functionality. - -A branch is a collection of related [staged changes](./stagedchange.md) that have been prepared for merging into the active database. A branch can be merged by executing its `commit()` method. Deleting a branch will delete all its related changes. - -## Fields - -### Name - -The branch's name. - -### User - -The user to which the branch belongs (optional). diff --git a/docs/models/extras/configtemplate.md b/docs/models/extras/configtemplate.md index b580d6885..6b245e5e9 100644 --- a/docs/models/extras/configtemplate.md +++ b/docs/models/extras/configtemplate.md @@ -12,10 +12,6 @@ See the [configuration rendering documentation](../../features/configuration-ren A unique human-friendly name. -### Weight - -A numeric value which influences the order in which context data is merged. Contexts with a lower weight are merged before those with a higher weight. - ### Data File Template code may optionally be sourced from a remote [data file](../core/datafile.md), which is synchronized from a remote data source. When designating a data file, there is no need to specify template code: It will be populated automatically from the data file. @@ -27,3 +23,27 @@ Jinja2 template code, if being defined locally rather than replicated from a dat ### Environment Parameters A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior. + +### MIME Type + +!!! info "This field was introduced in NetBox v4.3." + +The MIME type to indicate in the response when rendering the configuration template (optional). Defaults to `text/plain`. + +### File Name + +!!! info "This field was introduced in NetBox v4.3." + +The file name to give to the rendered export file (optional). + +### File Extension + +!!! info "This field was introduced in NetBox v4.3." + +The file extension to append to the file name in the response (optional). + +### As Attachment + +!!! info "This field was introduced in NetBox v4.3." + +If selected, the rendered content will be returned as a file attachment, rather than displayed directly in-browser (where supported). \ No newline at end of file diff --git a/docs/models/extras/exporttemplate.md b/docs/models/extras/exporttemplate.md index d2f9292c6..86e1ae04a 100644 --- a/docs/models/extras/exporttemplate.md +++ b/docs/models/extras/exporttemplate.md @@ -20,10 +20,20 @@ Template code may optionally be sourced from a remote [data file](../core/datafi Jinja2 template code for rendering the exported data. +### Environment Parameters + +!!! info "This field was introduced in NetBox v4.3." + +A dictionary of any additional parameters to pass when instantiating the [Jinja2 environment](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment). Jinja2 supports various optional parameters which can be used to modify its default behavior. + ### MIME Type The MIME type to indicate in the response when rendering the export template (optional). Defaults to `text/plain`. +### File Name + +The file name to give to the rendered export file (optional). + ### File Extension The file extension to append to the file name in the response (optional). diff --git a/docs/models/extras/stagedchange.md b/docs/models/extras/stagedchange.md deleted file mode 100644 index 0693a32d3..000000000 --- a/docs/models/extras/stagedchange.md +++ /dev/null @@ -1,29 +0,0 @@ -# Staged Changes - -!!! danger "Deprecated Feature" - This feature has been deprecated in NetBox v4.2 and will be removed in a future release. Please consider using the [netbox-branching plugin](https://github.com/netboxlabs/netbox-branching), which provides much more robust functionality. - -A staged change represents the creation of a new object or the modification or deletion of an existing object to be performed at some future point. Each change must be assigned to a [branch](./branch.md). - -Changes can be applied individually via the `apply()` method, however it is recommended to apply changes in bulk using the parent branch's `commit()` method. - -## Fields - -!!! warning - Staged changes are not typically created or manipulated directly, but rather effected through the use of the [`checkout()`](../../plugins/development/staged-changes.md) context manager. - -### Branch - -The [branch](./branch.md) to which this change belongs. - -### Action - -The type of action this change represents: `create`, `update`, or `delete`. - -### Object - -A generic foreign key referencing the existing object to which this change applies. - -### Data - -JSON representation of the changes being made to the object (not applicable for deletions). diff --git a/docs/models/extras/tableconfig.md b/docs/models/extras/tableconfig.md new file mode 100644 index 000000000..e5484ec64 --- /dev/null +++ b/docs/models/extras/tableconfig.md @@ -0,0 +1,43 @@ +# Table Configs + +This object represents the saved configuration of an object table in NetBox. Table configs can be crafted, saved, and shared among users to apply specific views within object lists. Each table config can specify which table columns to display, the order in which to display them, and which columns are used for sorting. + +For example, you might wish to create a table config for the devices list to assist in inventory tasks. This view might show the device name, location, serial number, and asset tag, but omit operational details like IP addresses. Once applied, this table config can be saved for reuse in future audits. + +## Fields + +### Name + +A human-friendly name for the table config. + +### User + +The user to which this filter belongs. The current user will be assigned automatically when saving a table config via the UI, and cannot be changed. + +### Object Type + +The type of NetBox object to which the table config pertains. + +### Table + +The name of the specific table to which the table config pertains. (Some NetBox object use multiple tables.) + +### Weight + +A numeric weight used to influence the order in which table configs are listed. Table configs with a lower weight will be listed before those with a higher weight. Table configs having the same weight will be ordered alphabetically. + +### Enabled + +Determines whether this table config can be used. Disabled table configs will not appear as options in the UI, however they will be included in API results. + +### Shared + +Determines whether this table config is intended for use by all users or only its owner. Note that deselecting this option does **not** hide the table config from other users; it is merely excluded from the list of available table configs in UI object list views. + +### Ordering + +A list of column names by which the table is to be ordered. If left blank, the table's default ordering will be used. + +### Columns + +A list of columns to be displayed in the table. The table will render these columns in the order they appear in the list. At least one column must be selected. diff --git a/docs/models/extras/tag.md b/docs/models/extras/tag.md index 39de48261..c4bc91b5a 100644 --- a/docs/models/extras/tag.md +++ b/docs/models/extras/tag.md @@ -16,6 +16,12 @@ A unique URL-friendly identifier. (This value will be used for filtering.) This The color to use when displaying the tag in the NetBox UI. +### Weight + +A numeric weight employed to influence the ordering of tags. Tags with a lower weight will be listed before those with higher weights. Values must be within the range **0** to **32767**. + +!!! info "This field was introduced in NetBox v4.3." + ### Object Types The assignment of a tag may be limited to a prescribed set of objects. For example, it may be desirable to limit the application of a specific tag to only devices and virtual machines. diff --git a/docs/models/ipam/iprange.md b/docs/models/ipam/iprange.md index 71f0884d9..fd439998a 100644 --- a/docs/models/ipam/iprange.md +++ b/docs/models/ipam/iprange.md @@ -2,6 +2,12 @@ This model represents an arbitrary range of individual IPv4 or IPv6 addresses, inclusive of its starting and ending addresses. For instance, the range 192.0.2.10 to 192.0.2.20 has eleven members. (The total member count is available as the `size` property on an IPRange instance.) Like [prefixes](./prefix.md) and [IP addresses](./ipaddress.md), each IP range may optionally be assigned to a [VRF](./vrf.md). +Each IP range can be marked as populated, which instructs NetBox to treat the range as though every IP address within it has been created (even though these individual IP addresses don't actually exist in the database). This can be helpful in scenarios where the management of a subset of IP addresses has been deferred to an external system of record, such as a DHCP server. NetBox will prohibit the creation of individual IP addresses within a range that has been marked as populated. + +An IP range can also be marked as utilized. This will cause its utilization to always be reported as 100% when viewing the range or when calculating the utilization of a parent prefix. (If not enabled, a range's utilization is calculated based on the number of IP addresses which have been created within it.) + +Typically, IP ranges marked as populated should also be marked as utilized, although there may be scenarios where this is undesirable (e.g. when reclaiming old IP space). An IP range which has been marked as populated but _not_ marked as utilized will always report a utilization of 0%, as it cannot contain child IP addresses. + ## Fields ### VRF @@ -29,6 +35,12 @@ The IP range's operational status. Note that the status of a range does _not_ ha !!! tip Additional statuses may be defined by setting `IPRange.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. +### Mark Populated + +!!! note "This field was added in NetBox v4.3." + +If enabled, NetBox will treat this IP range as being fully populated when calculating available IP space. It will also prevent the creation of IP addresses which fall within the declared range (and assigned VRF, if any). + ### Mark Utilized If enabled, the IP range will be considered 100% utilized regardless of how many IP addresses are defined within it. This is useful for documenting DHCP ranges, for example. diff --git a/docs/models/ipam/service.md b/docs/models/ipam/service.md index 316828b61..0d5f12a17 100644 --- a/docs/models/ipam/service.md +++ b/docs/models/ipam/service.md @@ -6,6 +6,15 @@ To aid in the efficient creation of services, users may opt to first create a [s ## Fields +### Parent + +The parent object to which the service is assigned. This must be one of [Device](../dcim/device.md), +[VirtualMachine](../virtualization/virtualmachine.md), or [FHRP Group](./fhrpgroup.md). + +!!! note "Changed in NetBox v4.3" + + Previously, `parent` was a property that pointed to either a Device or Virtual Machine. With the capability to assign services to FHRP groups, this is a unified in a concrete field. + ### Name A service or protocol name. diff --git a/docs/models/tenancy/contact.md b/docs/models/tenancy/contact.md index eac630180..f277ab499 100644 --- a/docs/models/tenancy/contact.md +++ b/docs/models/tenancy/contact.md @@ -4,9 +4,11 @@ A contact represents an individual or group that has been associated with an obj ## Fields -### Group +### Groups -The [contact group](./contactgroup.md) to which this contact is assigned (if any). +The [contact groups](./contactgroup.md) to which this contact is assigned (if any). + +!!! info "This field was renamed from `group` to `groups` in NetBox v4.3, and now supports the assignment of a contact to more than one group." ### Name diff --git a/docs/models/vpn/l2vpn.md b/docs/models/vpn/l2vpn.md index 1167c1c17..983095ef8 100644 --- a/docs/models/vpn/l2vpn.md +++ b/docs/models/vpn/l2vpn.md @@ -33,6 +33,19 @@ The technology employed in forming and operating the L2VPN. Choices include: !!! note Designating the type as VPWS, EPL, EP-LAN, EP-TREE will limit the L2VPN instance to two terminations. +### Status + +The operational status of the L2VPN. By default, the following statuses are available: + +* Active (default) +* Planned +* Faulty + +!!! tip "Custom L2VPN statuses" + Additional L2VPN statuses may be defined by setting `L2VPN.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter. + +!!! info "This field was introduced in NetBox v4.3." + ### Identifier An optional numeric identifier. This can be used to track a pseudowire ID, for example. diff --git a/docs/plugins/development/filtersets.md b/docs/plugins/development/filtersets.md index d803ce2f4..e19b3a733 100644 --- a/docs/plugins/development/filtersets.md +++ b/docs/plugins/development/filtersets.md @@ -1,6 +1,6 @@ # Filters & Filter Sets -Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI, REST API, or GraphQL API. NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. +Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. ## FilterSet Classes @@ -61,6 +61,11 @@ class MyModelViewSet(...): The `TagFilter` class is available for all models which support tag assignment (those which inherit from `NetBoxModel` or `TagsMixin`). This filter subclasses django-filter's `ModelMultipleChoiceFilter` to work with NetBox's `TaggedItem` class. +This class filters `tags` using the `slug` field. For example: + +`GET /api/dcim/sites/?tag=alpha&tag=bravo` + + ```python from django_filters import FilterSet from extras.filters import TagFilter @@ -68,3 +73,19 @@ from extras.filters import TagFilter class MyModelFilterSet(FilterSet): tag = TagFilter() ``` + +### TagIDFilter + +The `TagIDFilter` class is available for all models which support tag assignment (those which inherit from `NetBoxModel` or `TagsMixin`). This filter subclasses django-filter's `ModelMultipleChoiceFilter` to work with NetBox's `TaggedItem` class. + +This class filters `tags` using the `id` field. For example: + +`GET /api/dcim/sites/?tag_id=100&tag_id=200` + +```python +from django_filters import FilterSet +from extras.filters import TagIDFilter + +class MyModelFilterSet(FilterSet): + tag_id = TagIDFilter() +``` diff --git a/docs/plugins/development/index.md b/docs/plugins/development/index.md index 9dbf23e84..56bde5e41 100644 --- a/docs/plugins/development/index.md +++ b/docs/plugins/development/index.md @@ -103,6 +103,7 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i | `name` | Raw plugin name; same as the plugin's source directory | | `verbose_name` | Human-friendly name for the plugin | | `version` | Current release ([semantic versioning](https://semver.org/) is encouraged) | +| `release_track` | An alternate release track (e.g. `dev` or `beta`) to which a release belongs | | `description` | Brief description of the plugin's purpose | | `author` | Name of plugin's author | | `author_email` | Author's public email address | diff --git a/docs/plugins/development/models.md b/docs/plugins/development/models.md index 03cedda16..508c4ce89 100644 --- a/docs/plugins/development/models.md +++ b/docs/plugins/development/models.md @@ -117,6 +117,10 @@ For more information about database migrations, see the [Django documentation](h ::: netbox.models.features.CloningMixin +::: netbox.models.features.ContactsMixin + +!!! info "Plugin support for ContactsMixin was introduced in NetBox v4.3." + ::: netbox.models.features.CustomLinksMixin ::: netbox.models.features.CustomFieldsMixin @@ -125,9 +129,6 @@ For more information about database migrations, see the [Django documentation](h ::: netbox.models.features.EventRulesMixin -!!! note - `EventRulesMixin` was renamed from `WebhooksMixin` in NetBox v3.7. - ::: netbox.models.features.ExportTemplatesMixin ::: netbox.models.features.JobsMixin diff --git a/docs/plugins/development/navigation.md b/docs/plugins/development/navigation.md index 90b523473..b5e2694b4 100644 --- a/docs/plugins/development/navigation.md +++ b/docs/plugins/development/navigation.md @@ -64,13 +64,14 @@ item1 = PluginMenuItem( A `PluginMenuItem` has the following attributes: -| Attribute | Required | Description | -|---------------|----------|----------------------------------------------------------------------------------------------------------| -| `link` | Yes | Name of the URL path to which this menu item links | -| `link_text` | Yes | The text presented to the user | -| `permissions` | - | A list of permissions required to display this link | -| `staff_only` | - | Display only for users who have `is_staff` set to true (any specified permissions will also be required) | -| `buttons` | - | An iterable of PluginMenuButton instances to include | +| Attribute | Required | Description | +|-----------------|----------|----------------------------------------------------------------------------------------------------------| +| `link` | Yes | Name of the URL path to which this menu item links | +| `link_text` | Yes | The text presented to the user | +| `permissions` | - | A list of permissions required to display this link | +| `auth_required` | - | Display only for authenticated users | +| `staff_only` | - | Display only for users who have `is_staff` set to true (any specified permissions will also be required) | +| `buttons` | - | An iterable of PluginMenuButton instances to include | ## Menu Buttons diff --git a/docs/plugins/development/staged-changes.md b/docs/plugins/development/staged-changes.md deleted file mode 100644 index a8fd1d232..000000000 --- a/docs/plugins/development/staged-changes.md +++ /dev/null @@ -1,39 +0,0 @@ -# Staged Changes - -!!! danger "Deprecated Feature" - This feature has been deprecated in NetBox v4.2 and will be removed in a future release. Please consider using the [netbox-branching plugin](https://github.com/netboxlabs/netbox-branching), which provides much more robust functionality. - -NetBox provides a programmatic API to stage the creation, modification, and deletion of objects without actually committing those changes to the active database. This can be useful for performing a "dry run" of bulk operations, or preparing a set of changes for administrative approval, for example. - -To begin staging changes, first create a [branch](../../models/extras/branch.md): - -```python -from extras.models import Branch - -branch1 = Branch.objects.create(name='branch1') -``` - -Then, activate the branch using the `checkout()` context manager and begin making your changes. This initiates a new database transaction. - -```python -from extras.models import Branch -from netbox.staging import checkout - -branch1 = Branch.objects.get(name='branch1') -with checkout(branch1): - Site.objects.create(name='New Site', slug='new-site') - # ... -``` - -Upon exiting the context, the database transaction is automatically rolled back and your changes recorded as [staged changes](../../models/extras/stagedchange.md). Re-entering a branch will trigger a new database transaction and automatically apply any staged changes associated with the branch. - -To apply the changes within a branch, call the branch's `commit()` method: - -```python -from extras.models import Branch - -branch1 = Branch.objects.get(name='branch1') -branch1.commit() -``` - -Committing a branch is an all-or-none operation: Any exceptions will revert the entire set of changes. After successfully committing a branch, all its associated StagedChange objects are automatically deleted (however the branch itself will remain and can be reused). diff --git a/docs/plugins/development/views.md b/docs/plugins/development/views.md index e3740de59..43cc0ce82 100644 --- a/docs/plugins/development/views.md +++ b/docs/plugins/development/views.md @@ -198,6 +198,7 @@ Plugins can inject custom content into certain areas of core NetBox views. This | Method | View | Description | |---------------------|-------------|-----------------------------------------------------| +| `head()` | All | Custom HTML `` block includes | | `navbar()` | All | Inject content inside the top navigation bar | | `list_buttons()` | List view | Add buttons to the top of the page | | `buttons()` | Object view | Add buttons to the top of the page | diff --git a/docs/release-notes/index.md b/docs/release-notes/index.md index d996224c1..0d0b10092 100644 --- a/docs/release-notes/index.md +++ b/docs/release-notes/index.md @@ -10,6 +10,15 @@ Minor releases are published in April, August, and December of each calendar yea This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release. +#### [Version 4.3](./version-4.3.md) (May 2025) + +* Module Type Profiles & Custom Attributes ([#19002](https://github.com/netbox-community/netbox/issues/19002)) +* Reusable Table Configurations ([#14591](https://github.com/netbox-community/netbox/issues/14591)) +* Option to Treat IP Ranges as Fully Populated ([#9763](https://github.com/netbox-community/netbox/issues/9763)) +* Hierarchical Device Roles ([#18245](https://github.com/netbox-community/netbox/issues/18245)) +* Periodic Synchronization of Data Sources ([#18287](https://github.com/netbox-community/netbox/issues/18287)) +* Proxy Routing ([#18627](https://github.com/netbox-community/netbox/issues/18627)) + #### [Version 4.2](./version-4.2.md) (January 2025) * Assign Multiple MAC Addresses per Interface ([#4867](https://github.com/netbox-community/netbox/issues/4867)) diff --git a/docs/release-notes/version-4.3.md b/docs/release-notes/version-4.3.md new file mode 100644 index 000000000..07d6b66f1 --- /dev/null +++ b/docs/release-notes/version-4.3.md @@ -0,0 +1,134 @@ +## v4.3.0 (2025-05-01) + +### Breaking Changes + +* The GraphQL API Now uses an advanced syntax for filtering, to enable e.g. logical AND/OR filtering and custom field lookups. +* PostgreSQL 13 is no longer supported. NetBox v4.3 requires PostgreSQL 14.0 or later. +* The `ALLOW_TOKEN_RETRIEVAL` configuration parameter now defaults to False. +* The `device` and `virtual_machine` foreign keys on the Service model have been replaced with a generic `parent` relationship to support the assignment of services to FHRP groups as well. +* The `group` foreign key on the Contact model has been replaced with a many-to-many `groups` field. +* `django-storages` is now a required dependency. (It will be installed automatically on upgrade.) +* PluginTemplateExtension no longer supports registration via the singular `model` attribute (use `models` instead). +* The legacy staged changes functionality has been removed. + +### New Features + +#### Module Type Profiles & Custom Attributes ([#19002](https://github.com/netbox-community/netbox/issues/19002)) + +The new [module type profile](../models/dcim/moduletypeprofile.md) model enables users to declare custom profiles for module types, with the ability to define custom attributes for each profile according to its functional role. For example, a CPU module type might declare architecture and clock speed attributes; a hard disk profile might declare attributes for type and speed. + +Attributes can be declared on each profile using [JSON schema](https://json-schema.org/), which allows for attributes to be declared as strings (text), integers, decimals, booleans, or choice fields. Profile attributes render as individual form fields when modifying a module type. Several profiles have been included by default to serve as examples, however these may be modified or removed. + +#### Reusable Table Configurations ([#14591](https://github.com/netbox-community/netbox/issues/14591)) + +After modifying the displayed columns and/or ordering for a specific object table in the user interface, users now have the option to save that configuration so that it can be reused in the future. Similar to saved filters, table configs can be shared with other users to easily replicate table layouts crafted to serve specific use cases. + +#### Option to Treat IP Ranges as Fully Populated ([#9763](https://github.com/netbox-community/netbox/issues/9763)) + +A new `mark_populated` boolean field has been added to the IPRange model. If set to true, NetBox will consider the IP range to be fully populated, and will not permit the creation of individual IP addresses within the range. For example, you might defer the management of an IP range to an external DHCP server, and wish for NetBox to treat the range as a opaque monolithic block for planning and allocation purposes. + +#### Hierarchical Device Roles ([#18245](https://github.com/netbox-community/netbox/issues/18245)) + +Device roles can now be arranged hierarchically, with one role optionally serving as a parent to one or more child roles. For example, you might wish to create a generic "Server" role for devices with "Application Server" and "Database Server" roles beneath it. A device could then be assigned to any of these three roles. + +#### Periodic Synchronization of Data Sources ([#18287](https://github.com/netbox-community/netbox/issues/18287)) + +Data sources can now be configured to synchronize automatically at a specified interval, as indicated by the new `sync_interval` field. No additional system configuration is necessary to support this functionality; background jobs will be scheduled automatically by the RQ worker process. + +#### Proxy Routing ([#18627](https://github.com/netbox-community/netbox/issues/18627)) + +User can now declare one or more proxy routers via the `PROXY_ROUTERS` configuration parameter to control the use of specific proxy servers for various outbound connections. For example, it is now possible to configure NetBox to use different proxies based on the type of outbound traffic or its destination. + +### Enhancements + +* [#7598](https://github.com/netbox-community/netbox/issues/7598) - Adopt advanced query filtering in GraphQL API to support filtering by custom fields +* [#8423](https://github.com/netbox-community/netbox/issues/8423) - Enable assigning services to FHRP groups +* [#15842](https://github.com/netbox-community/netbox/issues/15842) - Introduce the `LOGIN_FORM_HIDDEN` configuration parameter +* [#16224](https://github.com/netbox-community/netbox/issues/16224) - Implement pagination support for the GraphQL API +* [#17170](https://github.com/netbox-community/netbox/issues/17170) - Enable the assignment of a contact to multiple contact groups +* [#17443](https://github.com/netbox-community/netbox/issues/17443) - Add a `file_name` field to the export template model +* [#17602](https://github.com/netbox-community/netbox/issues/17602) - Add a `comments` field to all nested group models (Region, SiteGroup, Location, ContactGroup, TenantGroup, and WirelessLANGroup) +* [#17608](https://github.com/netbox-community/netbox/issues/17608) - Add a `status` field to the L2VPN model +* [#17653](https://github.com/netbox-community/netbox/issues/17653) - Enable declaring Jinja environment parameters on export templates (similar to config templates) +* [#17793](https://github.com/netbox-community/netbox/issues/17793) - Introduce a REST API endpoint for tagged objects (`/api/extras/tagged-objects/`) +* [#17841](https://github.com/netbox-community/netbox/issues/17841) - Add a `weight` field to the Tag model to influence ordering +* [#18296](https://github.com/netbox-community/netbox/issues/18296) - Add a `tenant` field to the VLAN group model +* [#18352](https://github.com/netbox-community/netbox/issues/18352) - Add a `status` field to the power outlet model +* [#18417](https://github.com/netbox-community/netbox/issues/18417) - Add an `outer_height` field to the rack & rack type models +* [#18535](https://github.com/netbox-community/netbox/issues/18535) - The presence of incompatible plugins will no longer prevent NetBox from starting +* [#18780](https://github.com/netbox-community/netbox/issues/18780) - Introduce `DATABASES` and `DATABASE_ROUTERS` configuration parameters to enable defining connections to external databases (e.g. for plugins) +* [#18783](https://github.com/netbox-community/netbox/issues/18783) - Enable filtering all applicable models by tag ID +* [#18785](https://github.com/netbox-community/netbox/issues/18785) - Enable custom choices for rack, device, and module airflow +* [#18896](https://github.com/netbox-community/netbox/issues/18896) - Enable the use of remote storage for custom scripts + +### Plugins + +* [#16630](https://github.com/netbox-community/netbox/issues/16630) - Plugins can now inject content within the HTML `` block via the new `plugin_head()` method on PluginTemplateExtension +* [#17424](https://github.com/netbox-community/netbox/issues/17424) - Extend ViewTab with a `visible` argument to control tab rendering +* [#17857](https://github.com/netbox-community/netbox/issues/17857) - Added a `release_track` attribute to PluginConfig +* [#18305](https://github.com/netbox-community/netbox/issues/18305) - Introduce plugin support for ContactsMixin +* [#19073](https://github.com/netbox-community/netbox/issues/19073) - Allow installed plugins to be omitted from the plugins list + +### Other Changes + +* [#18071](https://github.com/netbox-community/netbox/issues/18071) - Removed legacy staged changed functionality in favor of the [netbox-branching](https://github.com/netboxlabs/netbox-branching) plugin +* [#18072](https://github.com/netbox-community/netbox/issues/18072) - Drop support for the singular `model` attribute on PluginTemplateExtension (use `models` instead) +* [#18191](https://github.com/netbox-community/netbox/issues/18191) - Remove redundant PostgreSQL indexes +* [#18236](https://github.com/netbox-community/netbox/issues/18236) - Upgrade the HTMX library to v2.0 +* [#18540](https://github.com/netbox-community/netbox/issues/18540) - Operational plugins are now recorded in the application registry +* [#18623](https://github.com/netbox-community/netbox/issues/18623) - Upgrade the Tabler CSS theme to v1.2 +* [#18743](https://github.com/netbox-community/netbox/issues/18743) - Upgrade Django to v5.2 +* [#18751](https://github.com/netbox-community/netbox/issues/18751) - Change the default value for `ALLOW_TOKEN_RETRIEVAL` to False +* [#18808](https://github.com/netbox-community/netbox/issues/18808) - Squashed migration dependencies have been altered to rectify an issue with Django's `sqlmigrate` management command +* [#18820](https://github.com/netbox-community/netbox/issues/18820) - PostgreSQL 13 is no longer supported +* [#19004](https://github.com/netbox-community/netbox/issues/19004) - The use of inventory items has been deprecated in favor of modules. Inventory items and roles may be removed in a future NetBox release. + +### REST API Changes + +* Added the following endpoints: + * `/api/extras/table-configs/` + * `/api/extras/tagged-objects/` + * `/api/dcim/module-type-profiles/` +* core.DataSource + * Added the optional `sync_interval` field +* dcim.DeviceRole + * Added the optional `parent` recursive foreign key field to effect hierarchical ordering + * Added a `comments` field +* dcim.Location + * Added a `comments` field +* dcim.ModuleType + * Added the optional `profile` foreign key to the new ModuleTypeProfile model +* dcim.PowerOutlet + * Added a `status` field +* dcim.Rack + * Added the optional `outer_height` field +* dcim.RackType + * Added the optional `outer_height` field +* dcim.Region + * Added a `comments` field +* dcim.SiteGroup + * Added a `comments` field +* extras.ConfigTemplate + * Added optional fields `mime_type`, `file_name`, `file_extension` and `as_attachment` +* extras.ExportTemplate + * Added optional fields `file_name` and `environment_params` (JSON) +* extras.Tag + * Added a `weight` field +* ipam.IPRange + * Added a `mark_populaed` boolean field +* ipam.L2VPN + * Added a `status` field +* ipam.Service + * Removed the `device` and `virtual_machine` foreign key fields + * Added the `parent_object_type`, `parent_object_id`, and (read-only) `parent` fields +* ipam.VLANGroup + * Added the optional `tenant` foreign key field +* tenancy.Contact + * Removed the `group` foreign key field + * Added the `groups` many-to-many field +* tenancy.ContactGroup + * Added a `comments` field +* tenancy.TenantGroup + * Added a `comments` field +* wireless.WirelessLANGroup + * Added a `comments` field diff --git a/mkdocs.yml b/mkdocs.yml index 75020d122..27526bd26 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -49,6 +49,7 @@ markdown_extensions: - admonition - attr_list - footnotes + - md_in_html - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg @@ -147,7 +148,6 @@ nav: - GraphQL API: 'plugins/development/graphql-api.md' - Background Jobs: 'plugins/development/background-jobs.md' - Dashboard Widgets: 'plugins/development/dashboard-widgets.md' - - Staged Changes: 'plugins/development/staged-changes.md' - Exceptions: 'plugins/development/exceptions.md' - Migrating to v4.0: 'plugins/development/migration-v4.md' - Administration: @@ -203,6 +203,7 @@ nav: - ModuleBay: 'models/dcim/modulebay.md' - ModuleBayTemplate: 'models/dcim/modulebaytemplate.md' - ModuleType: 'models/dcim/moduletype.md' + - ModuleTypeProfile: 'models/dcim/moduletypeprofile.md' - Platform: 'models/dcim/platform.md' - PowerFeed: 'models/dcim/powerfeed.md' - PowerOutlet: 'models/dcim/poweroutlet.md' @@ -223,7 +224,6 @@ nav: - VirtualDeviceContext: 'models/dcim/virtualdevicecontext.md' - Extras: - Bookmark: 'models/extras/bookmark.md' - - Branch: 'models/extras/branch.md' - ConfigContext: 'models/extras/configcontext.md' - ConfigTemplate: 'models/extras/configtemplate.md' - CustomField: 'models/extras/customfield.md' @@ -236,8 +236,8 @@ nav: - Notification: 'models/extras/notification.md' - NotificationGroup: 'models/extras/notificationgroup.md' - SavedFilter: 'models/extras/savedfilter.md' - - StagedChange: 'models/extras/stagedchange.md' - Subscription: 'models/extras/subscription.md' + - TableConfig: 'models/extras/tableconfig.md' - Tag: 'models/extras/tag.md' - Webhook: 'models/extras/webhook.md' - IPAM: @@ -309,6 +309,7 @@ nav: - git Cheat Sheet: 'development/git-cheat-sheet.md' - Release Notes: - Summary: 'release-notes/index.md' + - Version 4.3: 'release-notes/version-4.3.md' - Version 4.2: 'release-notes/version-4.2.md' - Version 4.1: 'release-notes/version-4.1.md' - Version 4.0: 'release-notes/version-4.0.md' diff --git a/netbox/account/migrations/0001_initial.py b/netbox/account/migrations/0001_initial.py index 72c079565..badd459ca 100644 --- a/netbox/account/migrations/0001_initial.py +++ b/netbox/account/migrations/0001_initial.py @@ -8,7 +8,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('users', '0004_netboxgroup_netboxuser'), + ('users', '0002_squashed_0004'), ] operations = [ diff --git a/netbox/account/views.py b/netbox/account/views.py index ced2d595c..f28d5eff5 100644 --- a/netbox/account/views.py +++ b/netbox/account/views.py @@ -91,10 +91,12 @@ class LoginView(View): if request.user.is_authenticated: logger = logging.getLogger('netbox.auth.login') return self.redirect_to_next(request, logger) + login_form_hidden = settings.LOGIN_FORM_HIDDEN return render(request, self.template_name, { 'form': form, 'auth_backends': self.get_auth_backends(request), + 'login_form_hidden': login_form_hidden, }) def post(self, request): diff --git a/netbox/circuits/graphql/enums.py b/netbox/circuits/graphql/enums.py new file mode 100644 index 000000000..c99fbda95 --- /dev/null +++ b/netbox/circuits/graphql/enums.py @@ -0,0 +1,16 @@ +import strawberry + +from circuits.choices import * + +__all__ = ( + 'CircuitStatusEnum', + 'CircuitTerminationSideEnum', + 'CircuitPriorityEnum', + 'VirtualCircuitTerminationRoleEnum', +) + + +CircuitPriorityEnum = strawberry.enum(CircuitPriorityChoices.as_enum(prefix='priority')) +CircuitStatusEnum = strawberry.enum(CircuitStatusChoices.as_enum('status')) +CircuitTerminationSideEnum = strawberry.enum(CircuitTerminationSideChoices.as_enum(prefix='side')) +VirtualCircuitTerminationRoleEnum = strawberry.enum(VirtualCircuitTerminationRoleChoices.as_enum(prefix='role')) diff --git a/netbox/circuits/graphql/filter_mixins.py b/netbox/circuits/graphql/filter_mixins.py new file mode 100644 index 000000000..3ae6fa82e --- /dev/null +++ b/netbox/circuits/graphql/filter_mixins.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django + +from netbox.graphql.filter_mixins import OrganizationalModelFilterMixin + +if TYPE_CHECKING: + from netbox.graphql.enums import ColorEnum + +__all__ = ( + 'BaseCircuitTypeFilterMixin', +) + + +@dataclass +class BaseCircuitTypeFilterMixin(OrganizationalModelFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index 7d066f428..966849fd0 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -1,7 +1,30 @@ -import strawberry_django +from datetime import date +from typing import Annotated, TYPE_CHECKING -from circuits import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup, DateFilterLookup + +from circuits import models +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from dcim.graphql.filter_mixins import CabledObjectModelFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import ( + DistanceFilterMixin, + ImageAttachmentFilterMixin, + OrganizationalModelFilterMixin, + PrimaryModelFilterMixin, +) +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin +from .filter_mixins import BaseCircuitTypeFilterMixin + +if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from dcim.graphql.filters import InterfaceFilter, LocationFilter, RegionFilter, SiteFilter, SiteGroupFilter + from ipam.graphql.filters import ASNFilter + from netbox.graphql.filter_lookups import IntegerLookup + from .enums import * __all__ = ( 'CircuitFilter', @@ -19,66 +42,183 @@ __all__ = ( @strawberry_django.filter(models.CircuitTermination, lookups=True) -@autotype_decorator(filtersets.CircuitTerminationFilterSet) -class CircuitTerminationFilter(BaseFilterMixin): - pass +class CircuitTerminationFilter( + BaseObjectTypeFilterMixin, + CustomFieldsFilterMixin, + TagsFilterMixin, + ChangeLogFilterMixin, + CabledObjectModelFilterMixin, +): + circuit: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + term_side: Annotated['CircuitTerminationSideEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + termination_id: ID | None = strawberry_django.filter_field() + port_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + xconnect_id: FilterLookup[str] | None = strawberry_django.filter_field() + pp_info: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + # Cached relations + _provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field(name='provider_network') + ) + _location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field(name='location') + ) + _region: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field(name='region') + ) + _site_group: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field(name='site_group') + ) + _site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field(name='site') + ) @strawberry_django.filter(models.Circuit, lookups=True) -@autotype_decorator(filtersets.CircuitFilterSet) -class CircuitFilter(BaseFilterMixin): - pass +class CircuitFilter( + ContactFilterMixin, + ImageAttachmentFilterMixin, + DistanceFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin +): + cid: FilterLookup[str] | None = strawberry_django.filter_field() + provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_id: ID | None = strawberry_django.filter_field() + provider_account: Annotated['ProviderAccountFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_account_id: ID | None = strawberry_django.filter_field() + type: Annotated['CircuitTypeFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + type_id: ID | None = strawberry_django.filter_field() + status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + install_date: DateFilterLookup[date] | None = strawberry_django.filter_field() + termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field() + commit_rate: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + terminations: Annotated['CircuitTerminationFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.CircuitType, lookups=True) -@autotype_decorator(filtersets.CircuitTypeFilterSet) -class CircuitTypeFilter(BaseFilterMixin): +class CircuitTypeFilter(BaseCircuitTypeFilterMixin): pass @strawberry_django.filter(models.CircuitGroup, lookups=True) -@autotype_decorator(filtersets.CircuitGroupFilterSet) -class CircuitGroupFilter(BaseFilterMixin): +class CircuitGroupFilter(TenancyFilterMixin, OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.CircuitGroupAssignment, lookups=True) -@autotype_decorator(filtersets.CircuitGroupAssignmentFilterSet) -class CircuitGroupAssignmentFilter(BaseFilterMixin): - pass +class CircuitGroupAssignmentFilter( + BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin +): + member_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + member_id: ID | None = strawberry_django.filter_field() + group: Annotated['CircuitGroupFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + priority: Annotated['CircuitPriorityEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Provider, lookups=True) -@autotype_decorator(filtersets.ProviderFilterSet) -class ProviderFilter(BaseFilterMixin): - pass +class ProviderFilter(ContactFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + circuits: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ProviderAccount, lookups=True) -@autotype_decorator(filtersets.ProviderAccountFilterSet) -class ProviderAccountFilter(BaseFilterMixin): - pass +class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilterMixin): + provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_id: ID | None = strawberry_django.filter_field() + account: FilterLookup[str] | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ProviderNetwork, lookups=True) -@autotype_decorator(filtersets.ProviderNetworkFilterSet) -class ProviderNetworkFilter(BaseFilterMixin): - pass +class ProviderNetworkFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_id: ID | None = strawberry_django.filter_field() + service_id: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VirtualCircuitType, lookups=True) -@autotype_decorator(filtersets.VirtualCircuitTypeFilterSet) -class VirtualCircuitTypeFilter(BaseFilterMixin): +class VirtualCircuitTypeFilter(BaseCircuitTypeFilterMixin): pass @strawberry_django.filter(models.VirtualCircuit, lookups=True) -@autotype_decorator(filtersets.VirtualCircuitFilterSet) -class VirtualCircuitFilter(BaseFilterMixin): - pass +class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + cid: FilterLookup[str] | None = strawberry_django.filter_field() + provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_network_id: ID | None = strawberry_django.filter_field() + provider_account: Annotated['ProviderAccountFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_account_id: ID | None = strawberry_django.filter_field() + type: Annotated['VirtualCircuitTypeFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + type_id: ID | None = strawberry_django.filter_field() + status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + group_assignments: Annotated['CircuitGroupAssignmentFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VirtualCircuitTermination, lookups=True) -@autotype_decorator(filtersets.VirtualCircuitTerminationFilterSet) -class VirtualCircuitTerminationFilter(BaseFilterMixin): - pass +class VirtualCircuitTerminationFilter( + BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin +): + virtual_circuit: Annotated['VirtualCircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_circuit_id: ID | None = strawberry_django.filter_field() + role: Annotated['VirtualCircuitTerminationRoleEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + interface: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_id: ID | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/circuits/graphql/types.py b/netbox/circuits/graphql/types.py index 96fcaa144..89d2a33b6 100644 --- a/netbox/circuits/graphql/types.py +++ b/netbox/circuits/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -10,11 +10,15 @@ from netbox.graphql.types import BaseObjectType, NetBoxObjectType, ObjectType, O from tenancy.graphql.types import TenantType from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import InterfaceType, LocationType, RegionType, SiteGroupType, SiteType + from ipam.graphql.types import ASNType + __all__ = ( - 'CircuitTerminationType', - 'CircuitType', 'CircuitGroupAssignmentType', 'CircuitGroupType', + 'CircuitTerminationType', + 'CircuitType', 'CircuitTypeType', 'ProviderType', 'ProviderAccountType', @@ -28,7 +32,8 @@ __all__ = ( @strawberry_django.type( models.Provider, fields='__all__', - filters=ProviderFilter + filters=ProviderFilter, + pagination=True ) class ProviderType(NetBoxObjectType, ContactsMixin): @@ -41,7 +46,8 @@ class ProviderType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.ProviderAccount, fields='__all__', - filters=ProviderAccountFilter + filters=ProviderAccountFilter, + pagination=True ) class ProviderAccountType(ContactsMixin, NetBoxObjectType): provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] @@ -52,7 +58,8 @@ class ProviderAccountType(ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.ProviderNetwork, fields='__all__', - filters=ProviderNetworkFilter + filters=ProviderNetworkFilter, + pagination=True ) class ProviderNetworkType(NetBoxObjectType): provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] @@ -62,8 +69,9 @@ class ProviderNetworkType(NetBoxObjectType): @strawberry_django.type( models.CircuitTermination, - exclude=('termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'), - filters=CircuitTerminationFilter + exclude=['termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'], + filters=CircuitTerminationFilter, + pagination=True ) class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType): circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')] @@ -82,7 +90,8 @@ class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, Ob @strawberry_django.type( models.CircuitType, fields='__all__', - filters=CircuitTypeFilter + filters=CircuitTypeFilter, + pagination=True ) class CircuitTypeType(OrganizationalObjectType): color: str @@ -93,7 +102,8 @@ class CircuitTypeType(OrganizationalObjectType): @strawberry_django.type( models.Circuit, fields='__all__', - filters=CircuitFilter + filters=CircuitFilter, + pagination=True ) class CircuitType(NetBoxObjectType, ContactsMixin): provider: ProviderType @@ -109,7 +119,8 @@ class CircuitType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.CircuitGroup, fields='__all__', - filters=CircuitGroupFilter + filters=CircuitGroupFilter, + pagination=True ) class CircuitGroupType(OrganizationalObjectType): tenant: TenantType | None @@ -117,8 +128,9 @@ class CircuitGroupType(OrganizationalObjectType): @strawberry_django.type( models.CircuitGroupAssignment, - exclude=('member_type', 'member_id'), - filters=CircuitGroupAssignmentFilter + exclude=['member_type', 'member_id'], + filters=CircuitGroupAssignmentFilter, + pagination=True ) class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')] @@ -134,7 +146,8 @@ class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): @strawberry_django.type( models.VirtualCircuitType, fields='__all__', - filters=VirtualCircuitTypeFilter + filters=VirtualCircuitTypeFilter, + pagination=True ) class VirtualCircuitTypeType(OrganizationalObjectType): color: str @@ -145,7 +158,8 @@ class VirtualCircuitTypeType(OrganizationalObjectType): @strawberry_django.type( models.VirtualCircuitTermination, fields='__all__', - filters=VirtualCircuitTerminationFilter + filters=VirtualCircuitTerminationFilter, + pagination=True ) class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): virtual_circuit: Annotated[ @@ -161,7 +175,8 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): @strawberry_django.type( models.VirtualCircuit, fields='__all__', - filters=VirtualCircuitFilter + filters=VirtualCircuitFilter, + pagination=True ) class VirtualCircuitType(NetBoxObjectType): provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"]) diff --git a/netbox/circuits/migrations/0002_squashed_0029.py b/netbox/circuits/migrations/0002_squashed_0029.py index cb61d8feb..0062575cd 100644 --- a/netbox/circuits/migrations/0002_squashed_0029.py +++ b/netbox/circuits/migrations/0002_squashed_0029.py @@ -5,11 +5,11 @@ import taggit.managers class Migration(migrations.Migration): dependencies = [ - ('dcim', '0001_initial'), + ('dcim', '0001_squashed'), ('contenttypes', '0002_remove_content_type_name'), - ('circuits', '0001_initial'), - ('extras', '0001_initial'), - ('tenancy', '0001_initial'), + ('circuits', '0001_squashed'), + ('extras', '0001_squashed'), + ('tenancy', '0001_squashed_0012'), ] replaces = [ diff --git a/netbox/circuits/migrations/0038_squashed_0042.py b/netbox/circuits/migrations/0038_squashed_0042.py index fa944b763..be07638b4 100644 --- a/netbox/circuits/migrations/0038_squashed_0042.py +++ b/netbox/circuits/migrations/0038_squashed_0042.py @@ -15,8 +15,8 @@ class Migration(migrations.Migration): ] dependencies = [ - ('circuits', '0037_new_cabling_models'), - ('dcim', '0160_populate_cable_ends'), + ('circuits', '0003_squashed_0037'), + ('dcim', '0160_squashed_0166'), ] operations = [ diff --git a/netbox/circuits/migrations/0043_circuittype_color.py b/netbox/circuits/migrations/0043_circuittype_color.py index 6c4dffeb6..400c419ef 100644 --- a/netbox/circuits/migrations/0043_circuittype_color.py +++ b/netbox/circuits/migrations/0043_circuittype_color.py @@ -6,7 +6,7 @@ import utilities.fields class Migration(migrations.Migration): dependencies = [ - ('circuits', '0042_provideraccount'), + ('circuits', '0038_squashed_0042'), ] operations = [ diff --git a/netbox/circuits/migrations/0047_circuittermination__termination.py b/netbox/circuits/migrations/0047_circuittermination__termination.py index f78e17ec3..4caa3a37d 100644 --- a/netbox/circuits/migrations/0047_circuittermination__termination.py +++ b/netbox/circuits/migrations/0047_circuittermination__termination.py @@ -39,9 +39,6 @@ class Migration(migrations.Migration): name='termination_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q( - ('model__in', ('region', 'sitegroup', 'site', 'location', 'providernetwork')) - ), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py index f8c0fd653..0418c26e5 100644 --- a/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py +++ b/netbox/circuits/migrations/0051_virtualcircuit_group_assignment.py @@ -51,7 +51,6 @@ class Migration(migrations.Migration): name='member_type', field=models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, - limit_choices_to=models.Q(('app_label', 'circuits'), ('model__in', ['circuit', 'virtualcircuit'])), related_name='+', to='contenttypes.contenttype', blank=True, @@ -68,7 +67,6 @@ class Migration(migrations.Migration): model_name='circuitgroupassignment', name='member_type', field=models.ForeignKey( - limit_choices_to=models.Q(('app_label', 'circuits'), ('model__in', ['circuit', 'virtualcircuit'])), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype' diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 8f5df7eb1..65f6323ae 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -182,7 +182,6 @@ class CircuitGroupAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, """ member_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS, on_delete=models.PROTECT, related_name='+' ) @@ -249,7 +248,6 @@ class CircuitTermination( termination_type = models.ForeignKey( to='contenttypes.ContentType', on_delete=models.PROTECT, - limit_choices_to=Q(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES), related_name='+', blank=True, null=True diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 8d5114ed9..62056cfbe 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -6,7 +6,6 @@ from django.utils.translation import gettext_lazy as _ from dcim.views import PathTraceView from ipam.models import ASN from netbox.views import generic -from tenancy.views import ObjectContactsView from utilities.forms import ConfirmationForm from utilities.query import count_related from utilities.views import GetRelatedModelsMixin, register_model_view @@ -89,11 +88,6 @@ class ProviderBulkDeleteView(generic.BulkDeleteView): table = tables.ProviderTable -@register_model_view(Provider, 'contacts') -class ProviderContactsView(ObjectContactsView): - queryset = Provider.objects.all() - - # # ProviderAccounts # @@ -156,11 +150,6 @@ class ProviderAccountBulkDeleteView(generic.BulkDeleteView): table = tables.ProviderAccountTable -@register_model_view(ProviderAccount, 'contacts') -class ProviderAccountContactsView(ObjectContactsView): - queryset = ProviderAccount.objects.all() - - # # Provider networks # @@ -433,11 +422,6 @@ class CircuitSwapTerminations(generic.ObjectEditView): }) -@register_model_view(Circuit, 'contacts') -class CircuitContactsView(ObjectContactsView): - queryset = Circuit.objects.all() - - # # Circuit terminations # diff --git a/netbox/core/api/serializers_/data.py b/netbox/core/api/serializers_/data.py index 2c155ba6b..3f2ddb2a0 100644 --- a/netbox/core/api/serializers_/data.py +++ b/netbox/core/api/serializers_/data.py @@ -26,8 +26,8 @@ class DataSourceSerializer(NetBoxModelSerializer): model = DataSource fields = [ 'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', - 'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated', 'last_synced', - 'file_count', + 'sync_interval', 'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated', + 'last_synced', 'file_count', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/core/apps.py b/netbox/core/apps.py index 283eed448..c081fb064 100644 --- a/netbox/core/apps.py +++ b/netbox/core/apps.py @@ -22,6 +22,7 @@ class CoreConfig(AppConfig): def ready(self): from core.api import schema # noqa: F401 + from core.checks import check_duplicate_indexes # noqa: F401 from netbox.models.features import register_models from . import data_backends, events, search # noqa: F401 from netbox import context_managers # noqa: F401 diff --git a/netbox/core/checks.py b/netbox/core/checks.py new file mode 100644 index 000000000..cab52a025 --- /dev/null +++ b/netbox/core/checks.py @@ -0,0 +1,41 @@ +from django.core.checks import Error, register, Tags +from django.db.models import Index, UniqueConstraint +from django.apps import apps + +__all__ = ( + 'check_duplicate_indexes', +) + + +@register(Tags.models) +def check_duplicate_indexes(app_configs, **kwargs): + """ + Check for an index which is redundant to a declared unique constraint. + """ + errors = [] + + for model in apps.get_models(): + if not (meta := getattr(model, "_meta", None)): + continue + + index_fields = { + tuple(index.fields) for index in getattr(meta, 'indexes', []) + if isinstance(index, Index) + } + constraint_fields = { + tuple(constraint.fields) for constraint in getattr(meta, 'constraints', []) + if isinstance(constraint, UniqueConstraint) + } + + # Find overlapping definitions + if duplicated := index_fields & constraint_fields: + for fields in duplicated: + errors.append( + Error( + f"Model '{model.__name__}' defines the same field set {fields} in both `Meta.indexes` and " + f"`Meta.constraints`.", + obj=model, + ) + ) + + return errors diff --git a/netbox/core/data_backends.py b/netbox/core/data_backends.py index 770a3b258..9ba1d5dfd 100644 --- a/netbox/core/data_backends.py +++ b/netbox/core/data_backends.py @@ -7,13 +7,13 @@ from pathlib import Path from urllib.parse import urlparse from django import forms -from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils.translation import gettext as _ from netbox.data_backends import DataBackend from netbox.utils import register_data_backend from utilities.constants import HTTP_PROXY_SUPPORTED_SCHEMAS, HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS +from utilities.proxy import resolve_proxies from utilities.socks import ProxyPoolManager from .exceptions import SyncError @@ -70,18 +70,18 @@ class GitBackend(DataBackend): # Initialize backend config config = ConfigDict() - self.use_socks = False + self.socks_proxy = None # Apply HTTP proxy (if configured) - if settings.HTTP_PROXIES: - if proxy := settings.HTTP_PROXIES.get(self.url_scheme, None): - if urlparse(proxy).scheme not in HTTP_PROXY_SUPPORTED_SCHEMAS: - raise ImproperlyConfigured(f"Unsupported Git DataSource proxy scheme: {urlparse(proxy).scheme}") + proxies = resolve_proxies(url=self.url, context={'client': self}) or {} + if proxy := proxies.get(self.url_scheme): + if urlparse(proxy).scheme not in HTTP_PROXY_SUPPORTED_SCHEMAS: + raise ImproperlyConfigured(f"Unsupported Git DataSource proxy scheme: {urlparse(proxy).scheme}") - if self.url_scheme in ('http', 'https'): - config.set("http", "proxy", proxy) - if urlparse(proxy).scheme in HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS: - self.use_socks = True + if self.url_scheme in ('http', 'https'): + config.set("http", "proxy", proxy) + if urlparse(proxy).scheme in HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS: + self.socks_proxy = proxy return config @@ -98,8 +98,8 @@ class GitBackend(DataBackend): } # check if using socks for proxy - if so need to use custom pool_manager - if self.use_socks: - clone_args['pool_manager'] = ProxyPoolManager(settings.HTTP_PROXIES.get(self.url_scheme)) + if self.socks_proxy: + clone_args['pool_manager'] = ProxyPoolManager(self.socks_proxy) if self.url_scheme in ('http', 'https'): if self.params.get('username'): @@ -147,7 +147,7 @@ class S3Backend(DataBackend): # Initialize backend config return Boto3Config( - proxies=settings.HTTP_PROXIES, + proxies=resolve_proxies(url=self.url, context={'client': self}), ) @contextmanager diff --git a/netbox/core/exceptions.py b/netbox/core/exceptions.py index 8412b0378..5790704c2 100644 --- a/netbox/core/exceptions.py +++ b/netbox/core/exceptions.py @@ -1,2 +1,9 @@ +from django.core.exceptions import ImproperlyConfigured + + class SyncError(Exception): pass + + +class IncompatiblePluginError(ImproperlyConfigured): + pass diff --git a/netbox/core/filtersets.py b/netbox/core/filtersets.py index 21fdaa4ab..42ec22350 100644 --- a/netbox/core/filtersets.py +++ b/netbox/core/filtersets.py @@ -29,6 +29,10 @@ class DataSourceFilterSet(NetBoxModelFilterSet): choices=DataSourceStatusChoices, null_value=None ) + sync_interval = django_filters.MultipleChoiceFilter( + choices=JobIntervalChoices, + null_value=None + ) class Meta: model = DataSource diff --git a/netbox/core/forms/bulk_edit.py b/netbox/core/forms/bulk_edit.py index c1f1fca4d..73618826d 100644 --- a/netbox/core/forms/bulk_edit.py +++ b/netbox/core/forms/bulk_edit.py @@ -1,6 +1,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from core.choices import JobIntervalChoices from core.models import * from netbox.forms import NetBoxModelBulkEditForm from netbox.utils import get_data_backend_choices @@ -29,6 +30,11 @@ class DataSourceBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + sync_interval = forms.ChoiceField( + choices=JobIntervalChoices, + required=False, + label=_('Sync interval') + ) comments = CommentField() parameters = forms.JSONField( label=_('Parameters'), @@ -42,8 +48,8 @@ class DataSourceBulkEditForm(NetBoxModelBulkEditForm): model = DataSource fieldsets = ( - FieldSet('type', 'enabled', 'description', 'comments', 'parameters', 'ignore_rules'), + FieldSet('type', 'enabled', 'description', 'sync_interval', 'parameters', 'ignore_rules', 'comments'), ) nullable_fields = ( - 'description', 'description', 'parameters', 'comments', 'parameters', 'ignore_rules', + 'description', 'description', 'sync_interval', 'parameters', 'parameters', 'ignore_rules' 'comments', ) diff --git a/netbox/core/forms/bulk_import.py b/netbox/core/forms/bulk_import.py index 78a859dcb..a5791c945 100644 --- a/netbox/core/forms/bulk_import.py +++ b/netbox/core/forms/bulk_import.py @@ -11,5 +11,6 @@ class DataSourceImportForm(NetBoxModelImportForm): class Meta: model = DataSource fields = ( - 'name', 'type', 'source_url', 'enabled', 'description', 'comments', 'parameters', 'ignore_rules', + 'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'parameters', 'ignore_rules', + 'comments', ) diff --git a/netbox/core/forms/filtersets.py b/netbox/core/forms/filtersets.py index f9564a76f..0f25932e0 100644 --- a/netbox/core/forms/filtersets.py +++ b/netbox/core/forms/filtersets.py @@ -27,7 +27,7 @@ class DataSourceFilterForm(NetBoxModelFilterSetForm): model = DataSource fieldsets = ( FieldSet('q', 'filter_id'), - FieldSet('type', 'status', name=_('Data Source')), + FieldSet('type', 'status', 'enabled', 'sync_interval', name=_('Data Source')), ) type = forms.MultipleChoiceField( label=_('Type'), @@ -46,6 +46,11 @@ class DataSourceFilterForm(NetBoxModelFilterSetForm): choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + sync_interval = forms.ChoiceField( + label=_('Sync interval'), + choices=JobIntervalChoices, + required=False + ) class DataFileFilterForm(NetBoxModelFilterSetForm): diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index a05377597..0a683a381 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -36,7 +36,7 @@ class DataSourceForm(NetBoxModelForm): class Meta: model = DataSource fields = [ - 'name', 'type', 'source_url', 'enabled', 'description', 'comments', 'ignore_rules', 'tags', + 'name', 'type', 'source_url', 'enabled', 'description', 'sync_interval', 'ignore_rules', 'comments', 'tags', ] widgets = { 'ignore_rules': forms.Textarea( @@ -51,7 +51,10 @@ class DataSourceForm(NetBoxModelForm): @property def fieldsets(self): fieldsets = [ - FieldSet('name', 'type', 'source_url', 'enabled', 'description', 'tags', 'ignore_rules', name=_('Source')), + FieldSet( + 'name', 'type', 'source_url', 'description', 'tags', 'ignore_rules', name=_('Source') + ), + FieldSet('enabled', 'sync_interval', name=_('Sync')), ] if self.backend_fields: fieldsets.append( diff --git a/netbox/core/graphql/filter_mixins.py b/netbox/core/graphql/filter_mixins.py new file mode 100644 index 000000000..670ec2ebb --- /dev/null +++ b/netbox/core/graphql/filter_mixins.py @@ -0,0 +1,36 @@ +from dataclasses import dataclass +from datetime import datetime +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django +from strawberry import ID +from strawberry_django import DatetimeFilterLookup + +if TYPE_CHECKING: + from .filters import * + +__all__ = ( + 'BaseFilterMixin', + 'BaseObjectTypeFilterMixin', + 'ChangeLogFilterMixin', +) + + +# @strawberry.input +class BaseFilterMixin: ... + + +@dataclass +class BaseObjectTypeFilterMixin(BaseFilterMixin): + id: ID | None = strawberry.UNSET + + +@dataclass +class ChangeLogFilterMixin(BaseFilterMixin): + id: ID | None = strawberry.UNSET + changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index 82da685a5..e5d44674a 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -1,28 +1,89 @@ -import strawberry_django +from datetime import datetime +from typing import Annotated, TYPE_CHECKING -from core import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +import strawberry +import strawberry_django +from django.contrib.contenttypes.models import ContentType as DjangoContentType +from strawberry.scalars import ID +from strawberry_django import DatetimeFilterLookup, FilterLookup + +from core import models +from core.graphql.filter_mixins import BaseFilterMixin +from netbox.graphql.filter_mixins import PrimaryModelFilterMixin + +if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerLookup, JSONFilter + from users.graphql.filters import UserFilter __all__ = ( 'DataFileFilter', 'DataSourceFilter', 'ObjectChangeFilter', + 'ContentTypeFilter', ) @strawberry_django.filter(models.DataFile, lookups=True) -@autotype_decorator(filtersets.DataFileFilterSet) class DataFileFilter(BaseFilterMixin): - pass + id: ID | None = strawberry_django.filter_field() + created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + source_id: ID | None = strawberry_django.filter_field() + path: FilterLookup[str] | None = strawberry_django.filter_field() + size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + hash: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DataSource, lookups=True) -@autotype_decorator(filtersets.DataSourceFilterSet) -class DataSourceFilter(BaseFilterMixin): - pass +class DataSourceFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + type: FilterLookup[str] | None = strawberry_django.filter_field() + source_url: FilterLookup[str] | None = strawberry_django.filter_field() + status: FilterLookup[str] | None = strawberry_django.filter_field() + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + ignore_rules: FilterLookup[str] | None = strawberry_django.filter_field() + parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + last_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + datafiles: Annotated['DataFileFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ObjectChange, lookups=True) -@autotype_decorator(filtersets.ObjectChangeFilterSet) class ObjectChangeFilter(BaseFilterMixin): - pass + id: ID | 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_name: FilterLookup[str] | None = strawberry_django.filter_field() + request_id: FilterLookup[str] | None = strawberry_django.filter_field() + action: FilterLookup[str] | None = strawberry_django.filter_field() + changed_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + changed_object_type_id: ID | None = strawberry_django.filter_field() + changed_object_id: ID | None = strawberry_django.filter_field() + related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + related_object_id: ID | None = strawberry_django.filter_field() + object_repr: FilterLookup[str] | None = strawberry_django.filter_field() + prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + postchange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + +@strawberry_django.filter(DjangoContentType, lookups=True) +class ContentTypeFilter(BaseFilterMixin): + id: ID | None = strawberry_django.filter_field() + app_label: FilterLookup[str] | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/core/graphql/mixins.py b/netbox/core/graphql/mixins.py index 5195b52a0..72191e6fd 100644 --- a/netbox/core/graphql/mixins.py +++ b/netbox/core/graphql/mixins.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django @@ -6,6 +6,9 @@ from django.contrib.contenttypes.models import ContentType from core.models import ObjectChange +if TYPE_CHECKING: + from netbox.core.graphql.types import ObjectChangeType + __all__ = ( 'ChangelogMixin', ) diff --git a/netbox/core/graphql/types.py b/netbox/core/graphql/types.py index 09385d7c1..ffaa24411 100644 --- a/netbox/core/graphql/types.py +++ b/netbox/core/graphql/types.py @@ -2,12 +2,14 @@ from typing import Annotated, List import strawberry import strawberry_django +from django.contrib.contenttypes.models import ContentType as DjangoContentType from core import models from netbox.graphql.types import BaseObjectType, NetBoxObjectType from .filters import * __all__ = ( + 'ContentType', 'DataFileType', 'DataSourceType', 'ObjectChangeType', @@ -17,7 +19,8 @@ __all__ = ( @strawberry_django.type( models.DataFile, exclude=['data',], - filters=DataFileFilter + filters=DataFileFilter, + pagination=True ) class DataFileType(BaseObjectType): source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] @@ -26,7 +29,8 @@ class DataFileType(BaseObjectType): @strawberry_django.type( models.DataSource, fields='__all__', - filters=DataSourceFilter + filters=DataSourceFilter, + pagination=True ) class DataSourceType(NetBoxObjectType): @@ -36,7 +40,17 @@ class DataSourceType(NetBoxObjectType): @strawberry_django.type( models.ObjectChange, fields='__all__', - filters=ObjectChangeFilter + filters=ObjectChangeFilter, + pagination=True ) class ObjectChangeType(BaseObjectType): pass + + +@strawberry_django.type( + DjangoContentType, + fields='__all__', + pagination=True +) +class ContentType: + pass diff --git a/netbox/core/jobs.py b/netbox/core/jobs.py index 891b1cbdb..b3dfaf1e7 100644 --- a/netbox/core/jobs.py +++ b/netbox/core/jobs.py @@ -5,6 +5,7 @@ import sys from django.conf import settings from netbox.jobs import JobRunner, system_job from netbox.search.backends import search_backend +from utilities.proxy import resolve_proxies from .choices import DataSourceStatusChoices, JobIntervalChoices from .exceptions import SyncError from .models import DataSource @@ -71,7 +72,7 @@ class SystemHousekeepingJob(JobRunner): url=settings.CENSUS_URL, params=census_data, timeout=3, - proxies=settings.HTTP_PROXIES + proxies=resolve_proxies(url=settings.CENSUS_URL) ) except requests.exceptions.RequestException: pass diff --git a/netbox/core/migrations/0006_datasource_type_remove_choices.py b/netbox/core/migrations/0006_datasource_type_remove_choices.py index 7c9914298..6a7fe2521 100644 --- a/netbox/core/migrations/0006_datasource_type_remove_choices.py +++ b/netbox/core/migrations/0006_datasource_type_remove_choices.py @@ -5,7 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('core', '0005_job_created_auto_now'), + ('core', '0001_squashed_0005'), ] operations = [ diff --git a/netbox/core/migrations/0014_datasource_sync_interval.py b/netbox/core/migrations/0014_datasource_sync_interval.py new file mode 100644 index 000000000..4b2e3ddd7 --- /dev/null +++ b/netbox/core/migrations/0014_datasource_sync_interval.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0013_job_data_encoder'), + ] + + operations = [ + migrations.AddField( + model_name='datasource', + name='sync_interval', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + ] diff --git a/netbox/core/migrations/0015_remove_redundant_indexes.py b/netbox/core/migrations/0015_remove_redundant_indexes.py new file mode 100644 index 000000000..b5ff0db6e --- /dev/null +++ b/netbox/core/migrations/0015_remove_redundant_indexes.py @@ -0,0 +1,23 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0014_datasource_sync_interval'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='autosyncrecord', + name='core_autosy_object__c17bac_idx', + ), + migrations.RemoveIndex( + model_name='datafile', + name='core_datafile_source_path', + ), + migrations.RemoveIndex( + model_name='managedfile', + name='core_managedfile_root_path', + ), + ] diff --git a/netbox/core/models/data.py b/netbox/core/models/data.py index 39ee8fa57..52a11c58e 100644 --- a/netbox/core/models/data.py +++ b/netbox/core/models/data.py @@ -59,6 +59,12 @@ class DataSource(JobsMixin, PrimaryModel): verbose_name=_('enabled'), default=True ) + sync_interval = models.PositiveSmallIntegerField( + verbose_name=_('sync interval'), + choices=JobIntervalChoices, + blank=True, + null=True + ) ignore_rules = models.TextField( verbose_name=_('ignore rules'), blank=True, @@ -304,9 +310,6 @@ class DataFile(models.Model): name='%(app_label)s_%(class)s_unique_source_path' ), ) - indexes = [ - models.Index(fields=('source', 'path'), name='core_datafile_source_path'), - ] verbose_name = _('data file') verbose_name_plural = _('data files') @@ -351,17 +354,6 @@ class DataFile(models.Model): return is_modified - def write_to_disk(self, path, overwrite=False): - """ - Write the object's data to disk at the specified path - """ - # Check whether file already exists - if os.path.isfile(path) and not overwrite: - raise FileExistsError() - - with open(path, 'wb+') as new_file: - new_file.write(self.data) - class AutoSyncRecord(models.Model): """ @@ -392,8 +384,5 @@ class AutoSyncRecord(models.Model): name='%(app_label)s_%(class)s_object' ), ) - indexes = ( - models.Index(fields=('object_type', 'object_id')), - ) verbose_name = _('auto sync record') verbose_name_plural = _('auto sync records') diff --git a/netbox/core/models/files.py b/netbox/core/models/files.py index cc446bac7..d60269b8b 100644 --- a/netbox/core/models/files.py +++ b/netbox/core/models/files.py @@ -1,13 +1,16 @@ import logging import os +from functools import cached_property from django.conf import settings from django.core.exceptions import ValidationError from django.db import models +from django.core.files.storage import storages from django.urls import reverse from django.utils.translation import gettext as _ from ..choices import ManagedFileRootPathChoices +from extras.storage import ScriptFileSystemStorage from netbox.models.features import SyncedDataMixin from utilities.querysets import RestrictedQuerySet @@ -55,9 +58,6 @@ class ManagedFile(SyncedDataMixin, models.Model): name='%(app_label)s_%(class)s_unique_root_path' ), ) - indexes = [ - models.Index(fields=('file_root', 'file_path'), name='core_managedfile_root_path'), - ] verbose_name = _('managed file') verbose_name_plural = _('managed files') @@ -76,15 +76,35 @@ class ManagedFile(SyncedDataMixin, models.Model): return os.path.join(self._resolve_root_path(), self.file_path) def _resolve_root_path(self): - return { - 'scripts': settings.SCRIPTS_ROOT, - 'reports': settings.REPORTS_ROOT, - }[self.file_root] + storage = self.storage + if isinstance(storage, ScriptFileSystemStorage): + return { + 'scripts': settings.SCRIPTS_ROOT, + 'reports': settings.REPORTS_ROOT, + }[self.file_root] + else: + return "" def sync_data(self): if self.data_file: self.file_path = os.path.basename(self.data_path) - self.data_file.write_to_disk(self.full_path, overwrite=True) + self._write_to_disk(self.full_path, overwrite=True) + + def _write_to_disk(self, path, overwrite=False): + """ + Write the object's data to disk at the specified path + """ + # Check whether file already exists + storage = self.storage + if storage.exists(path) and not overwrite: + raise FileExistsError() + + with storage.open(path, 'wb+') as new_file: + new_file.write(self.data) + + @cached_property + def storage(self): + return storages.create_storage(storages.backends["scripts"]) def clean(self): super().clean() @@ -104,8 +124,9 @@ class ManagedFile(SyncedDataMixin, models.Model): def delete(self, *args, **kwargs): # Delete file from disk + storage = self.storage try: - os.remove(self.full_path) + storage.delete(self.full_path) except FileNotFoundError: pass diff --git a/netbox/core/plugins.py b/netbox/core/plugins.py index 3775dcad4..0a5bd8fe3 100644 --- a/netbox/core/plugins.py +++ b/netbox/core/plugins.py @@ -9,7 +9,9 @@ from django.conf import settings from django.core.cache import cache from netbox.plugins import PluginConfig +from netbox.registry import registry from utilities.datetime import datetime_from_timestamp +from utilities.proxy import resolve_proxies USER_AGENT_STRING = f'NetBox/{settings.RELEASE.version} {settings.RELEASE.edition}' CACHE_KEY_CATALOG_FEED = 'plugins-catalog-feed' @@ -64,9 +66,11 @@ class Plugin: is_certified: bool = False release_latest: PluginVersion = field(default_factory=PluginVersion) release_recent_history: list[PluginVersion] = field(default_factory=list) - is_local: bool = False # extra field for locally installed plugins - is_installed: bool = False + is_local: bool = False # Indicates that the plugin is listed in settings.PLUGINS (i.e. installed) + is_loaded: bool = False # Indicates whether the plugin successfully loaded at launch installed_version: str = '' + netbox_min_version: str = '' + netbox_max_version: str = '' def get_local_plugins(plugins=None): @@ -80,6 +84,9 @@ def get_local_plugins(plugins=None): for plugin_name in settings.PLUGINS: plugin = importlib.import_module(plugin_name) plugin_config: PluginConfig = plugin.config + installed_version = plugin_config.version + if plugin_config.release_track: + installed_version = f'{installed_version}-{plugin_config.release_track}' if plugin_config.author: author = PluginAuthor( @@ -95,20 +102,29 @@ def get_local_plugins(plugins=None): tag_line=plugin_config.description, description_short=plugin_config.description, is_local=True, - is_installed=True, + is_loaded=plugin_name in registry['plugins']['installed'], + installed_version=installed_version, + netbox_min_version=plugin_config.min_version, + netbox_max_version=plugin_config.max_version, author=author, - installed_version=plugin_config.version, ) # Update catalog entries for local plugins, or add them to the list if not listed for k, v in local_plugins.items(): if k in plugins: - plugins[k].is_local = True - plugins[k].is_installed = True + plugins[k].is_local = v.is_local + plugins[k].is_loaded = v.is_loaded plugins[k].installed_version = v.installed_version else: plugins[k] = v + # Update plugin table config for hidden and static plugins + hidden = settings.PLUGINS_CATALOG_CONFIG.get('hidden', []) + static = settings.PLUGINS_CATALOG_CONFIG.get('static', []) + for k, v in plugins.items(): + v.hidden = k in hidden + v.static = k in static + return plugins @@ -125,10 +141,11 @@ def get_catalog_plugins(): def get_pages(): # TODO: pagination is currently broken in API payload = {'page': '1', 'per_page': '50'} + proxies = resolve_proxies(url=settings.PLUGIN_CATALOG_URL) first_page = session.get( settings.PLUGIN_CATALOG_URL, headers={'User-Agent': USER_AGENT_STRING}, - proxies=settings.HTTP_PROXIES, + proxies=proxies, timeout=3, params=payload ).json() @@ -140,7 +157,7 @@ def get_catalog_plugins(): next_page = session.get( settings.PLUGIN_CATALOG_URL, headers={'User-Agent': USER_AGENT_STRING}, - proxies=settings.HTTP_PROXIES, + proxies=proxies, timeout=3, params=payload ).json() diff --git a/netbox/core/signals.py b/netbox/core/signals.py index 0bed3fd45..4b537b2d4 100644 --- a/netbox/core/signals.py +++ b/netbox/core/signals.py @@ -8,16 +8,15 @@ from django.dispatch import receiver, Signal from django.utils.translation import gettext_lazy as _ from django_prometheus.models import model_deletes, model_inserts, model_updates -from core.choices import ObjectChangeActionChoices +from core.choices import JobStatusChoices, ObjectChangeActionChoices from core.events import * -from core.models import ObjectChange from extras.events import enqueue_event from extras.utils import run_validators from netbox.config import get_config from netbox.context import current_request, events_queue from netbox.models.features import ChangeLoggingMixin from utilities.exceptions import AbortRequest -from .models import ConfigRevision +from .models import ConfigRevision, DataSource, ObjectChange __all__ = ( 'clear_events', @@ -188,6 +187,25 @@ def clear_events_queue(sender, **kwargs): # DataSource handlers # +@receiver(post_save, sender=DataSource) +def enqueue_sync_job(instance, created, **kwargs): + """ + When a DataSource is saved, check its sync_interval and enqueue a sync job if appropriate. + """ + from .jobs import SyncDataSourceJob + + if instance.enabled and instance.sync_interval: + SyncDataSourceJob.enqueue_once(instance, interval=instance.sync_interval) + elif not created: + # Delete any previously scheduled recurring jobs for this DataSource + for job in SyncDataSourceJob.get_jobs(instance).defer('data').filter( + interval__isnull=False, + status=JobStatusChoices.STATUS_SCHEDULED + ): + # Call delete() per instance to ensure the associated background task is deleted as well + job.delete() + + @receiver(post_sync) def auto_sync(instance, **kwargs): """ diff --git a/netbox/core/tables/data.py b/netbox/core/tables/data.py index 4059ea9bc..5c6ccebcf 100644 --- a/netbox/core/tables/data.py +++ b/netbox/core/tables/data.py @@ -14,10 +14,10 @@ __all__ = ( class DataSourceTable(NetBoxTable): name = tables.Column( verbose_name=_('Name'), - linkify=True + linkify=True, ) type = BackendTypeColumn( - verbose_name=_('Type') + verbose_name=_('Type'), ) status = columns.ChoiceFieldColumn( verbose_name=_('Status'), @@ -25,20 +25,26 @@ class DataSourceTable(NetBoxTable): enabled = columns.BooleanColumn( verbose_name=_('Enabled'), ) - tags = columns.TagColumn( - url_name='core:datasource_list' + sync_interval = columns.ChoiceFieldColumn( + verbose_name=_('Sync interval'), + ) + last_synced = tables.DateTimeColumn( + verbose_name=_('Last Synced'), ) file_count = tables.Column( - verbose_name='Files' + verbose_name=_('Files'), + ) + tags = columns.TagColumn( + url_name='core:datasource_list', ) class Meta(NetBoxTable.Meta): model = DataSource fields = ( - 'pk', 'id', 'name', 'type', 'status', 'enabled', 'source_url', 'description', 'comments', 'parameters', - 'created', 'last_updated', 'file_count', + 'pk', 'id', 'name', 'type', 'status', 'enabled', 'source_url', 'description', 'sync_interval', 'comments', + 'parameters', 'last_synced', 'created', 'last_updated', 'file_count', ) - default_columns = ('pk', 'name', 'type', 'status', 'enabled', 'description', 'file_count') + default_columns = ('pk', 'name', 'type', 'status', 'enabled', 'description', 'sync_interval', 'file_count') class DataFileTable(NetBoxTable): diff --git a/netbox/core/tables/plugins.py b/netbox/core/tables/plugins.py index 88197dd21..e1b80af42 100644 --- a/netbox/core/tables/plugins.py +++ b/netbox/core/tables/plugins.py @@ -1,7 +1,10 @@ import django_tables2 as tables +from django.urls import reverse +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from netbox.tables import BaseTable, columns +from .template_code import PLUGIN_IS_INSTALLED __all__ = ( 'CatalogPluginTable', @@ -54,12 +57,15 @@ class CatalogPluginTable(BaseTable): verbose_name=_('Author') ) is_local = columns.BooleanColumn( + false_mark=None, verbose_name=_('Local') ) - is_installed = columns.BooleanColumn( - verbose_name=_('Installed') + is_installed = columns.TemplateColumn( + verbose_name=_('Active'), + template_code=PLUGIN_IS_INSTALLED ) is_certified = columns.BooleanColumn( + false_mark=None, verbose_name=_('Certified') ) created_at = columns.DateTimeColumn( @@ -88,3 +94,9 @@ class CatalogPluginTable(BaseTable): # List installed plugins first, then certified plugins, then # everything else (with each tranche ordered alphabetically) order_by = ('-is_installed', '-is_certified', 'name') + + def render_title_long(self, value, record): + if record.static: + return value + url = reverse('core:plugin', args=[record.config_name]) + return mark_safe(f"{value}") diff --git a/netbox/core/tables/template_code.py b/netbox/core/tables/template_code.py index c8f0058e7..9fc652c4c 100644 --- a/netbox/core/tables/template_code.py +++ b/netbox/core/tables/template_code.py @@ -14,3 +14,15 @@ OBJECTCHANGE_OBJECT = """ OBJECTCHANGE_REQUEST_ID = """ {{ value }} """ + +PLUGIN_IS_INSTALLED = """ +{% if record.is_local %} + {% if record.is_loaded %} + + {% else %} + + {% endif %} +{% else %} + +{% endif %} +""" diff --git a/netbox/core/tests/test_filtersets.py b/netbox/core/tests/test_filtersets.py index 310be1d0e..b7dfd516e 100644 --- a/netbox/core/tests/test_filtersets.py +++ b/netbox/core/tests/test_filtersets.py @@ -27,7 +27,8 @@ class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests): source_url='file:///var/tmp/source1/', status=DataSourceStatusChoices.NEW, enabled=True, - description='foobar1' + description='foobar1', + sync_interval=JobIntervalChoices.INTERVAL_HOURLY ), DataSource( name='Data Source 2', @@ -35,14 +36,16 @@ class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests): source_url='file:///var/tmp/source2/', status=DataSourceStatusChoices.SYNCING, enabled=True, - description='foobar2' + description='foobar2', + sync_interval=JobIntervalChoices.INTERVAL_DAILY ), DataSource( name='Data Source 3', type='git', source_url='https://example.com/git/source3', status=DataSourceStatusChoices.COMPLETED, - enabled=False + enabled=False, + sync_interval=JobIntervalChoices.INTERVAL_WEEKLY ), ) DataSource.objects.bulk_create(data_sources) @@ -73,6 +76,10 @@ class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'status': [DataSourceStatusChoices.NEW, DataSourceStatusChoices.SYNCING]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_sync_interval(self): + params = {'sync_interval': [JobIntervalChoices.INTERVAL_HOURLY, JobIntervalChoices.INTERVAL_DAILY]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class DataFileTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = DataFile.objects.all() diff --git a/netbox/core/views.py b/netbox/core/views.py index 494b95cde..1264c6c1b 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -22,6 +22,7 @@ from rq.worker_registration import clean_worker_registry from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs_from_status, requeue_rq_job, stop_rq_job from netbox.config import get_config, PARAMS +from netbox.registry import registry from netbox.views import generic from netbox.views.generic.base import BaseObjectView from netbox.views.generic.mixins import TableMixin @@ -560,7 +561,7 @@ class SystemView(UserPassesTestMixin, View): params = [param.name for param in PARAMS] data = { **stats, - 'plugins': settings.PLUGINS, + 'plugins': registry['plugins']['installed'], 'config': { k: getattr(config, k) for k in sorted(params) }, @@ -612,6 +613,8 @@ class PluginListView(BasePluginView): if q: plugins = [obj for obj in plugins if q.casefold() in obj.title_short.casefold()] + plugins = [plugin for plugin in plugins if not plugin.hidden] + table = CatalogPluginTable(plugins, user=request.user) table.configure(request) diff --git a/netbox/dcim/api/serializers_/device_components.py b/netbox/dcim/api/serializers_/device_components.py index b591030aa..468d75af9 100644 --- a/netbox/dcim/api/serializers_/device_components.py +++ b/netbox/dcim/api/serializers_/device_components.py @@ -152,14 +152,15 @@ class PowerOutletSerializer(NetBoxModelSerializer, CabledObjectSerializer, Conne required=False, allow_null=True ) + status = ChoiceField(choices=PowerOutletStatusChoices, required=False) class Meta: model = PowerOutlet fields = [ - 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', - 'feed_leg', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', - 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', 'tags', 'custom_fields', - 'created', 'last_updated', '_occupied', + 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'status', 'color', + 'power_port', 'feed_leg', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', + 'link_peers_type', 'connected_endpoints', 'connected_endpoints_type', 'connected_endpoints_reachable', + 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', ] brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied') diff --git a/netbox/dcim/api/serializers_/devicetypes.py b/netbox/dcim/api/serializers_/devicetypes.py index 0ce2af2f8..61e3833ec 100644 --- a/netbox/dcim/api/serializers_/devicetypes.py +++ b/netbox/dcim/api/serializers_/devicetypes.py @@ -4,8 +4,8 @@ from django.utils.translation import gettext as _ from rest_framework import serializers from dcim.choices import * -from dcim.models import DeviceType, ModuleType -from netbox.api.fields import ChoiceField, RelatedObjectCountField +from dcim.models import DeviceType, ModuleType, ModuleTypeProfile +from netbox.api.fields import AttributesField, ChoiceField, RelatedObjectCountField from netbox.api.serializers import NetBoxModelSerializer from netbox.choices import * from .manufacturers import ManufacturerSerializer @@ -13,6 +13,7 @@ from .platforms import PlatformSerializer __all__ = ( 'DeviceTypeSerializer', + 'ModuleTypeProfileSerializer', 'ModuleTypeSerializer', ) @@ -62,7 +63,23 @@ class DeviceTypeSerializer(NetBoxModelSerializer): brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description', 'device_count') +class ModuleTypeProfileSerializer(NetBoxModelSerializer): + + class Meta: + model = ModuleTypeProfile + fields = [ + 'id', 'url', 'display_url', 'display', 'name', 'description', 'schema', 'comments', 'tags', 'custom_fields', + 'created', 'last_updated', + ] + brief_fields = ('id', 'url', 'display', 'name', 'description') + + class ModuleTypeSerializer(NetBoxModelSerializer): + profile = ModuleTypeProfileSerializer( + nested=True, + required=False, + allow_null=True + ) manufacturer = ManufacturerSerializer( nested=True ) @@ -78,12 +95,17 @@ class ModuleTypeSerializer(NetBoxModelSerializer): required=False, allow_null=True ) + attributes = AttributesField( + source='attribute_data', + required=False, + allow_null=True + ) class Meta: model = ModuleType fields = [ - 'id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'part_number', 'airflow', - 'weight', 'weight_unit', 'description', 'comments', 'tags', 'custom_fields', - 'created', 'last_updated', + 'id', 'url', 'display_url', 'display', 'profile', 'manufacturer', 'model', 'part_number', 'airflow', + 'weight', 'weight_unit', 'description', 'attributes', 'comments', 'tags', 'custom_fields', 'created', + 'last_updated', ] - brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'description') + brief_fields = ('id', 'url', 'display', 'profile', 'manufacturer', 'model', 'description') diff --git a/netbox/dcim/api/serializers_/nested.py b/netbox/dcim/api/serializers_/nested.py index ea346cc63..0e9eaa52f 100644 --- a/netbox/dcim/api/serializers_/nested.py +++ b/netbox/dcim/api/serializers_/nested.py @@ -52,6 +52,13 @@ class NestedLocationSerializer(WritableNestedSerializer): fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'rack_count', '_depth'] +class NestedDeviceRoleSerializer(WritableNestedSerializer): + + class Meta: + model = models.DeviceRole + fields = ['id', 'url', 'display_url', 'display', 'name'] + + class NestedDeviceSerializer(WritableNestedSerializer): class Meta: diff --git a/netbox/dcim/api/serializers_/racks.py b/netbox/dcim/api/serializers_/racks.py index 1378c265a..4bc2900dc 100644 --- a/netbox/dcim/api/serializers_/racks.py +++ b/netbox/dcim/api/serializers_/racks.py @@ -70,8 +70,8 @@ class RackTypeSerializer(RackBaseSerializer): model = RackType fields = [ 'id', 'url', 'display_url', 'display', 'manufacturer', 'model', 'slug', 'description', 'form_factor', - 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'weight', - 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'comments', 'tags', + 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', + 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'mounting_depth', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'manufacturer', 'model', 'slug', 'description') @@ -129,9 +129,9 @@ class RackSerializer(RackBaseSerializer): fields = [ 'id', 'url', 'display_url', 'display', 'name', 'facility_id', 'site', 'location', 'tenant', 'status', 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'weight', - 'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', - 'airflow', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', - 'powerfeed_count', + 'max_weight', 'weight_unit', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', + 'mounting_depth', 'airflow', 'description', 'comments', 'tags', 'custom_fields', + 'created', 'last_updated', 'device_count', 'powerfeed_count', ] brief_fields = ('id', 'url', 'display', 'name', 'description', 'device_count') diff --git a/netbox/dcim/api/serializers_/roles.py b/netbox/dcim/api/serializers_/roles.py index 8f922da10..17eeaa949 100644 --- a/netbox/dcim/api/serializers_/roles.py +++ b/netbox/dcim/api/serializers_/roles.py @@ -1,7 +1,8 @@ from dcim.models import DeviceRole, InventoryItemRole from extras.api.serializers_.configtemplates import ConfigTemplateSerializer from netbox.api.fields import RelatedObjectCountField -from netbox.api.serializers import NetBoxModelSerializer +from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer +from .nested import NestedDeviceRoleSerializer __all__ = ( 'DeviceRoleSerializer', @@ -9,7 +10,8 @@ __all__ = ( ) -class DeviceRoleSerializer(NetBoxModelSerializer): +class DeviceRoleSerializer(NestedGroupModelSerializer): + parent = NestedDeviceRoleSerializer(required=False, allow_null=True, default=None) config_template = ConfigTemplateSerializer(nested=True, required=False, allow_null=True, default=None) # Related object counts @@ -19,10 +21,13 @@ class DeviceRoleSerializer(NetBoxModelSerializer): class Meta: model = DeviceRole fields = [ - 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', + 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'vm_role', 'config_template', 'parent', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'virtualmachine_count', + 'comments', '_depth', ] - brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count') + brief_fields = ( + 'id', 'url', 'display', 'name', 'slug', 'description', 'device_count', 'virtualmachine_count', '_depth' + ) class InventoryItemRoleSerializer(NetBoxModelSerializer): diff --git a/netbox/dcim/api/serializers_/sites.py b/netbox/dcim/api/serializers_/sites.py index b818cd954..90f7b5d35 100644 --- a/netbox/dcim/api/serializers_/sites.py +++ b/netbox/dcim/api/serializers_/sites.py @@ -27,7 +27,7 @@ class RegionSerializer(NestedGroupModelSerializer): model = Region fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', - 'created', 'last_updated', 'site_count', 'prefix_count', '_depth', + 'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth') @@ -41,7 +41,7 @@ class SiteGroupSerializer(NestedGroupModelSerializer): model = SiteGroup fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', - 'created', 'last_updated', 'site_count', 'prefix_count', '_depth', + 'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth') @@ -93,6 +93,6 @@ class LocationSerializer(NestedGroupModelSerializer): fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count', - 'prefix_count', '_depth', + 'prefix_count', 'comments', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth') diff --git a/netbox/dcim/api/urls.py b/netbox/dcim/api/urls.py index fc3740374..734ac13db 100644 --- a/netbox/dcim/api/urls.py +++ b/netbox/dcim/api/urls.py @@ -21,6 +21,7 @@ router.register('rack-reservations', views.RackReservationViewSet) router.register('manufacturers', views.ManufacturerViewSet) router.register('device-types', views.DeviceTypeViewSet) router.register('module-types', views.ModuleTypeViewSet) +router.register('module-type-profiles', views.ModuleTypeProfileViewSet) # Device type components router.register('console-port-templates', views.ConsolePortTemplateViewSet) diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 5ca851bca..710e55001 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -270,6 +270,12 @@ class DeviceTypeViewSet(NetBoxModelViewSet): filterset_class = filtersets.DeviceTypeFilterSet +class ModuleTypeProfileViewSet(NetBoxModelViewSet): + queryset = ModuleTypeProfile.objects.all() + serializer_class = serializers.ModuleTypeProfileSerializer + filterset_class = filtersets.ModuleTypeProfileFilterSet + + class ModuleTypeViewSet(NetBoxModelViewSet): queryset = ModuleType.objects.all() serializer_class = serializers.ModuleTypeSerializer diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index bf405707c..267966e10 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -128,14 +128,15 @@ class RackElevationDetailRenderChoices(ChoiceSet): class RackAirflowChoices(ChoiceSet): + key = 'Rack.airflow' FRONT_TO_REAR = 'front-to-rear' REAR_TO_FRONT = 'rear-to-front' - CHOICES = ( + CHOICES = [ (FRONT_TO_REAR, _('Front to rear')), (REAR_TO_FRONT, _('Rear to front')), - ) + ] # @@ -191,6 +192,7 @@ class DeviceStatusChoices(ChoiceSet): class DeviceAirflowChoices(ChoiceSet): + key = 'Device.airflow' AIRFLOW_FRONT_TO_REAR = 'front-to-rear' AIRFLOW_REAR_TO_FRONT = 'rear-to-front' @@ -203,7 +205,7 @@ class DeviceAirflowChoices(ChoiceSet): AIRFLOW_PASSIVE = 'passive' AIRFLOW_MIXED = 'mixed' - CHOICES = ( + CHOICES = [ (AIRFLOW_FRONT_TO_REAR, _('Front to rear')), (AIRFLOW_REAR_TO_FRONT, _('Rear to front')), (AIRFLOW_LEFT_TO_RIGHT, _('Left to right')), @@ -214,7 +216,7 @@ class DeviceAirflowChoices(ChoiceSet): (AIRFLOW_TOP_TO_BOTTOM, _('Top to bottom')), (AIRFLOW_PASSIVE, _('Passive')), (AIRFLOW_MIXED, _('Mixed')), - ) + ] # @@ -242,6 +244,7 @@ class ModuleStatusChoices(ChoiceSet): class ModuleAirflowChoices(ChoiceSet): + key = 'Module.airflow' FRONT_TO_REAR = 'front-to-rear' REAR_TO_FRONT = 'rear-to-front' @@ -250,14 +253,14 @@ class ModuleAirflowChoices(ChoiceSet): SIDE_TO_REAR = 'side-to-rear' PASSIVE = 'passive' - CHOICES = ( + CHOICES = [ (FRONT_TO_REAR, _('Front to rear')), (REAR_TO_FRONT, _('Rear to front')), (LEFT_TO_RIGHT, _('Left to right')), (RIGHT_TO_LEFT, _('Right to left')), (SIDE_TO_REAR, _('Side to rear')), (PASSIVE, _('Passive')), - ) + ] # @@ -1635,6 +1638,23 @@ class PowerFeedPhaseChoices(ChoiceSet): ) +# +# PowerOutlets +# +class PowerOutletStatusChoices(ChoiceSet): + key = 'PowerOutlet.status' + + STATUS_ENABLED = 'enabled' + STATUS_DISABLED = 'disabled' + STATUS_FAULTY = 'faulty' + + CHOICES = [ + (STATUS_ENABLED, _('Enabled'), 'green'), + (STATUS_DISABLED, _('Disabled'), 'red'), + (STATUS_FAULTY, _('Faulty'), 'gray'), + ] + + # # VDC # diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index e01c3f658..a31cf136d 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -11,7 +11,8 @@ from ipam.filtersets import PrimaryIPFilterSet from ipam.models import ASN, IPAddress, VLANTranslationPolicy, VRF from netbox.choices import ColorChoices from netbox.filtersets import ( - BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet, + AttributeFiltersMixin, BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet, NetBoxModelFilterSet, + OrganizationalModelFilterSet, ) from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet from tenancy.models import * @@ -58,6 +59,7 @@ __all__ = ( 'ModuleBayTemplateFilterSet', 'ModuleFilterSet', 'ModuleTypeFilterSet', + 'ModuleTypeProfileFilterSet', 'PathEndpointFilterSet', 'PlatformFilterSet', 'PowerConnectionFilterSet', @@ -81,7 +83,7 @@ __all__ = ( ) -class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet): +class RegionFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet): parent_id = django_filters.ModelMultipleChoiceFilter( queryset=Region.objects.all(), label=_('Parent region (ID)'), @@ -111,7 +113,7 @@ class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet): fields = ('id', 'name', 'slug', 'description') -class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet): +class SiteGroupFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet): parent_id = django_filters.ModelMultipleChoiceFilter( queryset=SiteGroup.objects.all(), label=_('Parent site group (ID)'), @@ -205,7 +207,7 @@ class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe return queryset.filter(qs_filter).distinct() -class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalModelFilterSet): +class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupModelFilterSet): region_id = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), field_name='site__region', @@ -275,13 +277,13 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM fields = ('id', 'name', 'slug', 'facility', 'description') def search(self, queryset, name, value): - if not value.strip(): - return queryset - return queryset.filter( - Q(name__icontains=value) | - Q(facility__icontains=value) | - Q(description__icontains=value) - ) + # extended in order to include querying on Location.facility + queryset = super().search(queryset, name, value) + + if value.strip(): + queryset = queryset | queryset.model.objects.filter(facility__icontains=value) + + return queryset class RackRoleFilterSet(OrganizationalModelFilterSet): @@ -312,8 +314,8 @@ class RackTypeFilterSet(NetBoxModelFilterSet): class Meta: model = RackType fields = ( - 'id', 'model', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth', - 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', + 'id', 'model', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height', + 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', ) def search(self, queryset, name, value): @@ -425,8 +427,8 @@ class RackFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe model = Rack fields = ( 'id', 'name', 'facility_id', 'asset_tag', 'u_height', 'starting_unit', 'desc_units', 'outer_width', - 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', - 'description', + 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', + 'weight_unit', 'description', ) def search(self, queryset, name, value): @@ -673,7 +675,33 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet): return queryset.exclude(inventoryitemtemplates__isnull=value) -class ModuleTypeFilterSet(NetBoxModelFilterSet): +class ModuleTypeProfileFilterSet(NetBoxModelFilterSet): + + class Meta: + model = ModuleTypeProfile + fields = ('id', 'name', 'description') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) | + Q(comments__icontains=value) + ) + + +class ModuleTypeFilterSet(AttributeFiltersMixin, NetBoxModelFilterSet): + profile_id = django_filters.ModelMultipleChoiceFilter( + queryset=ModuleTypeProfile.objects.all(), + label=_('Profile (ID)'), + ) + profile = django_filters.ModelMultipleChoiceFilter( + field_name='profile__name', + queryset=ModuleTypeProfile.objects.all(), + to_field_name='name', + label=_('Profile (name)'), + ) manufacturer_id = django_filters.ModelMultipleChoiceFilter( queryset=Manufacturer.objects.all(), label=_('Manufacturer (ID)'), @@ -921,6 +949,29 @@ class DeviceRoleFilterSet(OrganizationalModelFilterSet): queryset=ConfigTemplate.objects.all(), label=_('Config template (ID)'), ) + parent_id = django_filters.ModelMultipleChoiceFilter( + queryset=DeviceRole.objects.all(), + label=_('Parent device role (ID)'), + ) + parent = django_filters.ModelMultipleChoiceFilter( + field_name='parent__slug', + queryset=DeviceRole.objects.all(), + to_field_name='slug', + label=_('Parent device role (slug)'), + ) + ancestor_id = TreeNodeMultipleChoiceFilter( + queryset=DeviceRole.objects.all(), + field_name='parent', + lookup_expr='in', + label=_('Parent device role (ID)'), + ) + ancestor = TreeNodeMultipleChoiceFilter( + queryset=DeviceRole.objects.all(), + field_name='parent', + lookup_expr='in', + to_field_name='slug', + label=_('Parent device role (slug)'), + ) class Meta: model = DeviceRole @@ -989,14 +1040,16 @@ class DeviceFilterSet( queryset=DeviceType.objects.all(), label=_('Device type (ID)'), ) - role_id = django_filters.ModelMultipleChoiceFilter( - field_name='role_id', + role_id = TreeNodeMultipleChoiceFilter( + field_name='role', queryset=DeviceRole.objects.all(), + lookup_expr='in', label=_('Role (ID)'), ) - role = django_filters.ModelMultipleChoiceFilter( - field_name='role__slug', + role = TreeNodeMultipleChoiceFilter( queryset=DeviceRole.objects.all(), + field_name='role', + lookup_expr='in', to_field_name='slug', label=_('Role (slug)'), ) @@ -1664,11 +1717,15 @@ class PowerOutletFilterSet( queryset=PowerPort.objects.all(), label=_('Power port (ID)'), ) + status = django_filters.MultipleChoiceFilter( + choices=PowerOutletStatusChoices, + null_value=None + ) class Meta: model = PowerOutlet fields = ( - 'id', 'name', 'label', 'feed_leg', 'description', 'color', 'mark_connected', 'cable_end', + 'id', 'name', 'status', 'label', 'feed_leg', 'description', 'color', 'mark_connected', 'cable_end', ) diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index e50804df8..098c1a58e 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -14,7 +14,9 @@ from netbox.forms import NetBoxModelBulkEditForm from tenancy.models import Tenant from users.models import User from utilities.forms import BulkEditForm, add_blank_choice, form_from_model -from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField +from utilities.forms.fields import ( + ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, +) from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions from virtualization.models import Cluster @@ -46,6 +48,7 @@ __all__ = ( 'ModuleBayBulkEditForm', 'ModuleBayTemplateBulkEditForm', 'ModuleTypeBulkEditForm', + 'ModuleTypeProfileBulkEditForm', 'PlatformBulkEditForm', 'PowerFeedBulkEditForm', 'PowerOutletBulkEditForm', @@ -78,12 +81,13 @@ class RegionBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = Region fieldsets = ( FieldSet('parent', 'description'), ) - nullable_fields = ('parent', 'description') + nullable_fields = ('parent', 'description', 'comments') class SiteGroupBulkEditForm(NetBoxModelBulkEditForm): @@ -97,12 +101,13 @@ class SiteGroupBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = SiteGroup fieldsets = ( FieldSet('parent', 'description'), ) - nullable_fields = ('parent', 'description') + nullable_fields = ('parent', 'description', 'comments') class SiteBulkEditForm(NetBoxModelBulkEditForm): @@ -197,12 +202,13 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = Location fieldsets = ( FieldSet('site', 'parent', 'status', 'tenant', 'description'), ) - nullable_fields = ('parent', 'tenant', 'description') + nullable_fields = ('parent', 'tenant', 'description', 'comments') class RackRoleBulkEditForm(NetBoxModelBulkEditForm): @@ -257,6 +263,11 @@ class RackTypeBulkEditForm(NetBoxModelBulkEditForm): required=False, min_value=1 ) + outer_height = forms.IntegerField( + label=_('Outer height'), + required=False, + min_value=1 + ) outer_depth = forms.IntegerField( label=_('Outer depth'), required=False, @@ -299,7 +310,7 @@ class RackTypeBulkEditForm(NetBoxModelBulkEditForm): fieldsets = ( FieldSet('manufacturer', 'description', 'form_factor', 'width', 'u_height', name=_('Rack Type')), FieldSet( - InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), + InlineFields('outer_width', 'outer_height', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), 'mounting_depth', name=_('Dimensions') @@ -307,7 +318,7 @@ class RackTypeBulkEditForm(NetBoxModelBulkEditForm): FieldSet('starting_unit', 'desc_units', name=_('Numbering')), ) nullable_fields = ( - 'outer_width', 'outer_depth', 'outer_unit', 'weight', + 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', ) @@ -401,6 +412,11 @@ class RackBulkEditForm(NetBoxModelBulkEditForm): required=False, min_value=1 ) + outer_height = forms.IntegerField( + label=_('Outer height'), + required=False, + min_value=1 + ) outer_depth = forms.IntegerField( label=_('Outer depth'), required=False, @@ -448,15 +464,13 @@ class RackBulkEditForm(NetBoxModelBulkEditForm): fieldsets = ( FieldSet('status', 'role', 'tenant', 'serial', 'asset_tag', 'rack_type', 'description', name=_('Rack')), FieldSet('region', 'site_group', 'site', 'location', name=_('Location')), - FieldSet( - 'form_factor', 'width', 'u_height', 'desc_units', 'airflow', 'outer_width', 'outer_depth', 'outer_unit', - 'mounting_depth', name=_('Hardware') - ), + FieldSet('outer_width', 'outer_height', 'outer_depth', 'outer_unit', name=_('Outer Dimensions')), + FieldSet('form_factor', 'width', 'u_height', 'desc_units', 'airflow', 'mounting_depth', name=_('Hardware')), FieldSet('weight', 'max_weight', 'weight_unit', name=_('Weight')), ) nullable_fields = ( - 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'weight', - 'max_weight', 'weight_unit', 'description', 'comments', + 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_height', 'outer_depth', + 'outer_unit', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', ) @@ -563,7 +577,31 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm): nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments') +class ModuleTypeProfileBulkEditForm(NetBoxModelBulkEditForm): + schema = JSONField( + label=_('Schema'), + required=False + ) + description = forms.CharField( + label=_('Description'), + max_length=200, + required=False + ) + comments = CommentField() + + model = ModuleTypeProfile + fieldsets = ( + FieldSet('name', 'description', 'schema', name=_('Profile')), + ) + nullable_fields = ('description', 'comments') + + class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm): + profile = DynamicModelChoiceField( + label=_('Profile'), + queryset=ModuleTypeProfile.objects.all(), + required=False + ) manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), @@ -598,17 +636,22 @@ class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm): model = ModuleType fieldsets = ( - FieldSet('manufacturer', 'part_number', 'description', name=_('Module Type')), + FieldSet('profile', 'manufacturer', 'part_number', 'description', name=_('Module Type')), FieldSet( 'airflow', InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), name=_('Chassis') ), ) - nullable_fields = ('part_number', 'weight', 'weight_unit', 'description', 'comments') + nullable_fields = ('part_number', 'weight', 'weight_unit', 'profile', 'description', 'comments') class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm): + parent = DynamicModelChoiceField( + label=_('Parent'), + queryset=DeviceRole.objects.all(), + required=False, + ) color = ColorField( label=_('Color'), required=False @@ -628,12 +671,13 @@ class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = DeviceRole fieldsets = ( - FieldSet('color', 'vm_role', 'config_template', 'description'), + FieldSet('parent', 'color', 'vm_role', 'config_template', 'description'), ) - nullable_fields = ('color', 'config_template', 'description') + nullable_fields = ('parent', 'color', 'config_template', 'description', 'comments') class PlatformBulkEditForm(NetBoxModelBulkEditForm): @@ -1379,7 +1423,10 @@ class PowerPortBulkEditForm( class PowerOutletBulkEditForm( ComponentBulkEditForm, - form_from_model(PowerOutlet, ['label', 'type', 'color', 'feed_leg', 'power_port', 'mark_connected', 'description']) + form_from_model( + PowerOutlet, + ['label', 'type', 'status', 'color', 'feed_leg', 'power_port', 'mark_connected', 'description'] + ) ): mark_connected = forms.NullBooleanField( label=_('Mark connected'), @@ -1389,7 +1436,7 @@ class PowerOutletBulkEditForm( model = PowerOutlet fieldsets = ( - FieldSet('module', 'type', 'label', 'description', 'mark_connected', 'color'), + FieldSet('module', 'type', 'label', 'status', 'description', 'mark_connected', 'color'), FieldSet('feed_leg', 'power_port', name=_('Power')), ) nullable_fields = ('module', 'label', 'type', 'feed_leg', 'power_port', 'description') diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index cb36e94bf..3ad4ced91 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -39,6 +39,7 @@ __all__ = ( 'ModuleImportForm', 'ModuleBayImportForm', 'ModuleTypeImportForm', + 'ModuleTypeProfileImportForm', 'PlatformImportForm', 'PowerFeedImportForm', 'PowerOutletImportForm', @@ -68,7 +69,7 @@ class RegionImportForm(NetBoxModelImportForm): class Meta: model = Region - fields = ('name', 'slug', 'parent', 'description', 'tags') + fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments') class SiteGroupImportForm(NetBoxModelImportForm): @@ -82,7 +83,7 @@ class SiteGroupImportForm(NetBoxModelImportForm): class Meta: model = SiteGroup - fields = ('name', 'slug', 'parent', 'description') + fields = ('name', 'slug', 'parent', 'description', 'comments', 'tags') class SiteImportForm(NetBoxModelImportForm): @@ -160,7 +161,10 @@ class LocationImportForm(NetBoxModelImportForm): class Meta: model = Location - fields = ('site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description', 'tags') + fields = ( + 'site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description', + 'tags', 'comments', + ) def __init__(self, data=None, *args, **kwargs): super().__init__(data, *args, **kwargs) @@ -219,7 +223,7 @@ class RackTypeImportForm(NetBoxModelImportForm): model = RackType fields = ( 'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', - 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', + 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags', ) @@ -304,7 +308,7 @@ class RackImportForm(NetBoxModelImportForm): model = Rack fields = ( 'site', 'location', 'name', 'facility_id', 'tenant', 'status', 'role', 'rack_type', 'form_factor', 'serial', - 'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', + 'asset_tag', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', 'description', 'comments', 'tags', ) @@ -424,7 +428,22 @@ class DeviceTypeImportForm(NetBoxModelImportForm): ] +class ModuleTypeProfileImportForm(NetBoxModelImportForm): + + class Meta: + model = ModuleTypeProfile + fields = [ + 'name', 'description', 'schema', 'comments', 'tags', + ] + + class ModuleTypeImportForm(NetBoxModelImportForm): + profile = forms.ModelChoiceField( + label=_('Profile'), + queryset=ModuleTypeProfile.objects.all(), + to_field_name='name', + required=False + ) manufacturer = forms.ModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all(), @@ -457,6 +476,16 @@ class ModuleTypeImportForm(NetBoxModelImportForm): class DeviceRoleImportForm(NetBoxModelImportForm): + parent = CSVModelChoiceField( + label=_('Parent'), + queryset=DeviceRole.objects.all(), + required=False, + to_field_name='name', + help_text=_('Parent Device Role'), + error_messages={ + 'invalid_choice': _('Device role not found.'), + } + ) config_template = CSVModelChoiceField( label=_('Config template'), queryset=ConfigTemplate.objects.all(), @@ -468,7 +497,9 @@ class DeviceRoleImportForm(NetBoxModelImportForm): class Meta: model = DeviceRole - fields = ('name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags') + fields = ( + 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags' + ) class PlatformImportForm(NetBoxModelImportForm): diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 5d5d25f96..813a578d6 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -39,6 +39,7 @@ __all__ = ( 'ModuleFilterForm', 'ModuleBayFilterForm', 'ModuleTypeFilterForm', + 'ModuleTypeProfileFilterForm', 'PlatformFilterForm', 'PowerConnectionFilterForm', 'PowerFeedFilterForm', @@ -602,11 +603,19 @@ class DeviceTypeFilterForm(NetBoxModelFilterSetForm): ) +class ModuleTypeProfileFilterForm(NetBoxModelFilterSetForm): + model = ModuleTypeProfile + fieldsets = ( + FieldSet('q', 'filter_id', 'tag'), + ) + selector_fields = ('filter_id', 'q') + + class ModuleTypeFilterForm(NetBoxModelFilterSetForm): model = ModuleType fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('manufacturer_id', 'part_number', 'airflow', name=_('Hardware')), + FieldSet('profile_id', 'manufacturer_id', 'part_number', 'airflow', name=_('Hardware')), FieldSet( 'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports', name=_('Components') @@ -614,6 +623,11 @@ class ModuleTypeFilterForm(NetBoxModelFilterSetForm): FieldSet('weight', 'weight_unit', name=_('Weight')), ) selector_fields = ('filter_id', 'q', 'manufacturer_id') + profile_id = DynamicModelMultipleChoiceField( + queryset=ModuleTypeProfile.objects.all(), + required=False, + label=_('Profile') + ) manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, @@ -689,6 +703,11 @@ class DeviceRoleFilterForm(NetBoxModelFilterSetForm): required=False, label=_('Config template') ) + parent_id = DynamicModelMultipleChoiceField( + queryset=DeviceRole.objects.all(), + required=False, + label=_('Parent') + ) tag = TagFilterField(model) @@ -1353,7 +1372,7 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): model = PowerOutlet fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('name', 'label', 'type', 'color', name=_('Attributes')), + FieldSet('name', 'label', 'type', 'color', 'status', name=_('Attributes')), FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id', name=_('Location')), FieldSet( 'device_type_id', 'device_role_id', 'device_id', 'device_status', 'virtual_chassis_id', @@ -1371,6 +1390,11 @@ class PowerOutletFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): label=_('Color'), required=False ) + status = forms.MultipleChoiceField( + label=_('Status'), + choices=PowerOutletStatusChoices, + required=False + ) class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm): diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 92ddc18fd..d8cff372f 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1,5 +1,6 @@ from django import forms from django.contrib.contenttypes.models import ContentType +from django.core.validators import EMPTY_VALUES from django.utils.translation import gettext_lazy as _ from timezone_field import TimeZoneFormField @@ -18,6 +19,7 @@ from utilities.forms.fields import ( ) from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups from utilities.forms.widgets import APISelect, ClearableFileInput, HTMXSelect, NumberWithOptions, SelectWithPK +from utilities.jsonschema import JSONSchemaProperty from virtualization.models import Cluster, VMInterface from wireless.models import WirelessLAN, WirelessLANGroup from .common import InterfaceCommonForm, ModuleCommonForm @@ -48,6 +50,7 @@ __all__ = ( 'ModuleBayForm', 'ModuleBayTemplateForm', 'ModuleTypeForm', + 'ModuleTypeProfileForm', 'PlatformForm', 'PopulateDeviceBayForm', 'PowerFeedForm', @@ -78,6 +81,7 @@ class RegionForm(NetBoxModelForm): required=False ) slug = SlugField() + comments = CommentField() fieldsets = ( FieldSet('parent', 'name', 'slug', 'description', 'tags'), @@ -86,7 +90,7 @@ class RegionForm(NetBoxModelForm): class Meta: model = Region fields = ( - 'parent', 'name', 'slug', 'description', 'tags', + 'parent', 'name', 'slug', 'description', 'tags', 'comments', ) @@ -97,6 +101,7 @@ class SiteGroupForm(NetBoxModelForm): required=False ) slug = SlugField() + comments = CommentField() fieldsets = ( FieldSet('parent', 'name', 'slug', 'description', 'tags'), @@ -105,7 +110,7 @@ class SiteGroupForm(NetBoxModelForm): class Meta: model = SiteGroup fields = ( - 'parent', 'name', 'slug', 'description', 'tags', + 'parent', 'name', 'slug', 'description', 'comments', 'tags', ) @@ -179,6 +184,7 @@ class LocationForm(TenancyForm, NetBoxModelForm): } ) slug = SlugField() + comments = CommentField() fieldsets = ( FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')), @@ -188,7 +194,8 @@ class LocationForm(TenancyForm, NetBoxModelForm): class Meta: model = Location fields = ( - 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'tags', + 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', + 'facility', 'tags', 'comments', ) @@ -222,7 +229,7 @@ class RackTypeForm(NetBoxModelForm): FieldSet('manufacturer', 'model', 'slug', 'description', 'form_factor', 'tags', name=_('Rack Type')), FieldSet( 'width', 'u_height', - InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), + InlineFields('outer_width', 'outer_height', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), 'mounting_depth', name=_('Dimensions') ), @@ -233,8 +240,8 @@ class RackTypeForm(NetBoxModelForm): model = RackType fields = [ 'manufacturer', 'model', 'slug', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', - 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', - 'description', 'comments', 'tags', + 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', + 'weight_unit', 'description', 'comments', 'tags', ] @@ -279,8 +286,8 @@ class RackForm(TenancyForm, NetBoxModelForm): fields = [ 'site', 'location', 'name', 'facility_id', 'tenant_group', 'tenant', 'status', 'role', 'serial', 'asset_tag', 'rack_type', 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', - 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'weight_unit', - 'description', 'comments', 'tags', + 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'weight', 'max_weight', + 'weight_unit', 'description', 'comments', 'tags', ] def __init__(self, *args, **kwargs): @@ -302,7 +309,8 @@ class RackForm(TenancyForm, NetBoxModelForm): *self.fieldsets, FieldSet( 'form_factor', 'width', 'starting_unit', 'u_height', - InlineFields('outer_width', 'outer_depth', 'outer_unit', label=_('Outer Dimensions')), + InlineFields('outer_width', 'outer_height', 'outer_depth', 'outer_unit', + label=_('Outer Dimensions')), InlineFields('weight', 'max_weight', 'weight_unit', label=_('Weight')), 'mounting_depth', 'desc_units', name=_('Dimensions') ), @@ -399,25 +407,104 @@ class DeviceTypeForm(NetBoxModelForm): } +class ModuleTypeProfileForm(NetBoxModelForm): + schema = JSONField( + label=_('Schema'), + required=False, + help_text=_("Enter a valid JSON schema to define supported attributes.") + ) + comments = CommentField() + + fieldsets = ( + FieldSet('name', 'description', 'schema', 'tags', name=_('Profile')), + ) + + class Meta: + model = ModuleTypeProfile + fields = [ + 'name', 'description', 'schema', 'comments', 'tags', + ] + + class ModuleTypeForm(NetBoxModelForm): + profile = forms.ModelChoiceField( + queryset=ModuleTypeProfile.objects.all(), + label=_('Profile'), + required=False, + widget=HTMXSelect() + ) manufacturer = DynamicModelChoiceField( label=_('Manufacturer'), queryset=Manufacturer.objects.all() ) comments = CommentField() - fieldsets = ( - FieldSet('manufacturer', 'model', 'part_number', 'description', 'tags', name=_('Module Type')), - FieldSet('airflow', 'weight', 'weight_unit', name=_('Chassis')) - ) + @property + def fieldsets(self): + return [ + FieldSet('manufacturer', 'model', 'part_number', 'description', 'tags', name=_('Module Type')), + FieldSet('airflow', 'weight', 'weight_unit', name=_('Hardware')), + FieldSet('profile', *self.attr_fields, name=_('Profile & Attributes')) + ] class Meta: model = ModuleType fields = [ - 'manufacturer', 'model', 'part_number', 'airflow', 'weight', 'weight_unit', 'description', + 'profile', 'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'comments', 'tags', ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Track profile-specific attribute fields + self.attr_fields = [] + + # Retrieve assigned ModuleTypeProfile, if any + if not (profile_id := get_field_value(self, 'profile')): + return + if not (profile := ModuleTypeProfile.objects.filter(pk=profile_id).first()): + return + + # Extend form with fields for profile attributes + for attr, form_field in self._get_attr_form_fields(profile).items(): + field_name = f'attr_{attr}' + self.attr_fields.append(field_name) + self.fields[field_name] = form_field + if self.instance.attribute_data: + self.fields[field_name].initial = self.instance.attribute_data.get(attr) + + @staticmethod + def _get_attr_form_fields(profile): + """ + Return a dictionary mapping of attribute names to form fields, suitable for extending + the form per the selected ModuleTypeProfile. + """ + if not profile.schema: + return {} + + properties = profile.schema.get('properties', {}) + required_fields = profile.schema.get('required', []) + + attr_fields = {} + for name, options in properties.items(): + prop = JSONSchemaProperty(**options) + attr_fields[name] = prop.to_form_field(name, required=name in required_fields) + + return dict(sorted(attr_fields.items())) + + def _post_clean(self): + + # Compile attribute data from the individual form fields + if self.cleaned_data.get('profile'): + self.instance.attribute_data = { + name[5:]: self.cleaned_data[name] # Remove the attr_ prefix + for name in self.attr_fields + if self.cleaned_data.get(name) not in EMPTY_VALUES + } + + return super()._post_clean() + class DeviceRoleForm(NetBoxModelForm): config_template = DynamicModelChoiceField( @@ -426,17 +513,24 @@ class DeviceRoleForm(NetBoxModelForm): required=False ) slug = SlugField() + parent = DynamicModelChoiceField( + label=_('Parent'), + queryset=DeviceRole.objects.all(), + required=False, + ) + comments = CommentField() fieldsets = ( FieldSet( - 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags', name=_('Device Role') + 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', + 'tags', name=_('Device Role') ), ) class Meta: model = DeviceRole fields = [ - 'name', 'slug', 'color', 'vm_role', 'config_template', 'description', 'tags', + 'name', 'slug', 'parent', 'color', 'vm_role', 'config_template', 'description', 'comments', 'tags', ] @@ -1339,7 +1433,7 @@ class PowerOutletForm(ModularDeviceComponentForm): fieldsets = ( FieldSet( - 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', + 'device', 'module', 'name', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'mark_connected', 'description', 'tags', ), ) @@ -1347,7 +1441,7 @@ class PowerOutletForm(ModularDeviceComponentForm): class Meta: model = PowerOutlet fields = [ - 'device', 'module', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'mark_connected', + 'device', 'module', 'name', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'mark_connected', 'description', 'tags', ] diff --git a/netbox/dcim/graphql/enums.py b/netbox/dcim/graphql/enums.py new file mode 100644 index 000000000..5f888cfb5 --- /dev/null +++ b/netbox/dcim/graphql/enums.py @@ -0,0 +1,75 @@ +import strawberry + +from dcim.choices import * + +__all__ = ( + 'CableEndEnum', + 'CableLengthUnitEnum', + 'CableTypeEnum', + 'ConsolePortSpeedEnum', + 'ConsolePortTypeEnum', + 'DeviceAirflowEnum', + 'DeviceFaceEnum', + 'DeviceStatusEnum', + 'InterfaceDuplexEnum', + 'InterfaceModeEnum', + 'InterfacePoEModeEnum', + 'InterfacePoETypeEnum', + 'InterfaceTypeEnum', + 'InventoryItemStatusEnum', + 'LinkStatusEnum', + 'LocationStatusEnum', + 'ModuleAirflowEnum', + 'ModuleStatusEnum', + 'PortTypeEnum', + 'PowerFeedPhaseEnum', + 'PowerFeedStatusEnum', + 'PowerFeedSupplyEnum', + 'PowerFeedTypeEnum', + 'PowerOutletFeedLegEnum', + 'PowerOutletTypeEnum', + 'PowerPortTypeEnum', + 'RackAirflowEnum', + 'RackDimensionUnitEnum', + 'RackFormFactorEnum', + 'RackStatusEnum', + 'RackWidthEnum', + 'SiteStatusEnum', + 'SubdeviceRoleEnum', + 'VirtualDeviceContextStatusEnum', +) + +CableEndEnum = strawberry.enum(CableEndChoices.as_enum(prefix='side')) +CableLengthUnitEnum = strawberry.enum(CableLengthUnitChoices.as_enum(prefix='unit')) +CableTypeEnum = strawberry.enum(CableTypeChoices.as_enum(prefix='type')) +ConsolePortSpeedEnum = strawberry.enum(ConsolePortSpeedChoices.as_enum(prefix='speed')) +ConsolePortTypeEnum = strawberry.enum(ConsolePortTypeChoices.as_enum(prefix='type')) +DeviceAirflowEnum = strawberry.enum(DeviceAirflowChoices.as_enum(prefix='airflow')) +DeviceFaceEnum = strawberry.enum(DeviceFaceChoices.as_enum(prefix='face')) +DeviceStatusEnum = strawberry.enum(DeviceStatusChoices.as_enum(prefix='status')) +InterfaceDuplexEnum = strawberry.enum(InterfaceDuplexChoices.as_enum(prefix='duplex')) +InterfaceModeEnum = strawberry.enum(InterfaceModeChoices.as_enum(prefix='mode')) +InterfacePoEModeEnum = strawberry.enum(InterfacePoEModeChoices.as_enum(prefix='mode')) +InterfacePoETypeEnum = strawberry.enum(InterfacePoETypeChoices.as_enum()) +InterfaceTypeEnum = strawberry.enum(InterfaceTypeChoices.as_enum(prefix='type')) +InventoryItemStatusEnum = strawberry.enum(InventoryItemStatusChoices.as_enum(prefix='status')) +LinkStatusEnum = strawberry.enum(LinkStatusChoices.as_enum(prefix='status')) +LocationStatusEnum = strawberry.enum(LocationStatusChoices.as_enum(prefix='status')) +ModuleAirflowEnum = strawberry.enum(ModuleAirflowChoices.as_enum()) +ModuleStatusEnum = strawberry.enum(ModuleStatusChoices.as_enum(prefix='status')) +PortTypeEnum = strawberry.enum(PortTypeChoices.as_enum(prefix='type')) +PowerFeedPhaseEnum = strawberry.enum(PowerFeedPhaseChoices.as_enum(prefix='phase')) +PowerFeedStatusEnum = strawberry.enum(PowerFeedStatusChoices.as_enum(prefix='status')) +PowerFeedSupplyEnum = strawberry.enum(PowerFeedSupplyChoices.as_enum(prefix='supply')) +PowerFeedTypeEnum = strawberry.enum(PowerFeedTypeChoices.as_enum(prefix='type')) +PowerOutletFeedLegEnum = strawberry.enum(PowerOutletFeedLegChoices.as_enum(prefix='feed_leg')) +PowerOutletTypeEnum = strawberry.enum(PowerOutletTypeChoices.as_enum(prefix='type')) +PowerPortTypeEnum = strawberry.enum(PowerPortTypeChoices.as_enum(prefix='type')) +RackAirflowEnum = strawberry.enum(RackAirflowChoices.as_enum()) +RackDimensionUnitEnum = strawberry.enum(RackDimensionUnitChoices.as_enum(prefix='unit')) +RackFormFactorEnum = strawberry.enum(RackFormFactorChoices.as_enum(prefix='type')) +RackStatusEnum = strawberry.enum(RackStatusChoices.as_enum(prefix='status')) +RackWidthEnum = strawberry.enum(RackWidthChoices.as_enum(prefix='width')) +SiteStatusEnum = strawberry.enum(SiteStatusChoices.as_enum(prefix='status')) +SubdeviceRoleEnum = strawberry.enum(SubdeviceRoleChoices.as_enum(prefix='role')) +VirtualDeviceContextStatusEnum = strawberry.enum(VirtualDeviceContextStatusChoices.as_enum(prefix='status')) diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py new file mode 100644 index 000000000..25379ad7f --- /dev/null +++ b/netbox/dcim/graphql/filter_mixins.py @@ -0,0 +1,148 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django +from strawberry import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseFilterMixin, ChangeLogFilterMixin +from core.graphql.filters import ContentTypeFilter +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin +from .enums import * + +if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerLookup + from extras.graphql.filters import ConfigTemplateFilter + from ipam.graphql.filters import VLANFilter, VLANTranslationPolicyFilter + from .filters import * + +__all__ = ( + 'CabledObjectModelFilterMixin', + 'ComponentModelFilterMixin', + 'ComponentTemplateFilterMixin', + 'InterfaceBaseFilterMixin', + 'ModularComponentModelFilterMixin', + 'ModularComponentTemplateFilterMixin', + 'RackBaseFilterMixin', + 'RenderConfigFilterMixin', + 'ScopedFilterMixin', +) + + +@dataclass +class ScopedFilterMixin(BaseFilterMixin): + scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + scope_id: ID | None = strawberry_django.filter_field() + + +@dataclass +class ComponentModelFilterMixin(NetBoxModelFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class ModularComponentModelFilterMixin(ComponentModelFilterMixin): + module: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + module_id: ID | None = strawberry_django.filter_field() + inventory_items: Annotated['InventoryItemFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class CabledObjectModelFilterMixin(BaseFilterMixin): + cable: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + cable_id: ID | None = strawberry_django.filter_field() + cable_end: CableEndEnum | None = strawberry_django.filter_field() + mark_connected: FilterLookup[bool] | None = strawberry_django.filter_field() + + +@dataclass +class ComponentTemplateFilterMixin(ChangeLogFilterMixin): + device_type: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_type_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class ModularComponentTemplateFilterMixin(ComponentTemplateFilterMixin): + module_type: Annotated['ModuleTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class RenderConfigFilterMixin(BaseFilterMixin): + config_template: Annotated['ConfigTemplateFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + config_template_id: ID | None = strawberry_django.filter_field() + + +@dataclass +class InterfaceBaseFilterMixin(BaseFilterMixin): + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + mtu: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + mode: InterfaceModeEnum | None = strawberry_django.filter_field() + bridge: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + bridge_id: ID | None = strawberry_django.filter_field() + untagged_vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tagged_vlans: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + qinq_svlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_translation_policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None \ + = strawberry_django.filter_field() + primary_mac_address: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_mac_address_id: ID | None = strawberry_django.filter_field() + + +@dataclass +class RackBaseFilterMixin(WeightFilterMixin, PrimaryModelFilterMixin): + width: Annotated['RackWidthEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + u_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + starting_unit: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + desc_units: FilterLookup[bool] | None = strawberry_django.filter_field() + outer_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + outer_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + outer_depth: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + outer_unit: Annotated['RackDimensionUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + mounting_depth: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + max_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 94f2c6d38..77e7a53b9 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -1,7 +1,46 @@ -import strawberry_django +from typing import Annotated, TYPE_CHECKING -from dcim import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import ChangeLogFilterMixin +from dcim import models +from extras.graphql.filter_mixins import ConfigContextFilterMixin +from netbox.graphql.filter_mixins import ( + PrimaryModelFilterMixin, + OrganizationalModelFilterMixin, + NestedGroupModelFilterMixin, + ImageAttachmentFilterMixin, + WeightFilterMixin, +) +from tenancy.graphql.filter_mixins import TenancyFilterMixin, ContactFilterMixin +from .filter_mixins import ( + CabledObjectModelFilterMixin, + ComponentModelFilterMixin, + ComponentTemplateFilterMixin, + InterfaceBaseFilterMixin, + ModularComponentModelFilterMixin, + ModularComponentTemplateFilterMixin, + RackBaseFilterMixin, + RenderConfigFilterMixin, +) + +if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from extras.graphql.filters import ConfigTemplateFilter, ImageAttachmentFilter + from ipam.graphql.filters import ( + ASNFilter, FHRPGroupAssignmentFilter, IPAddressFilter, PrefixFilter, VLANGroupFilter, VRFFilter, + ) + from netbox.graphql.enums import ColorEnum + from netbox.graphql.filter_lookups import FloatLookup, IntegerArrayLookup, IntegerLookup, TreeNodeFilter + from users.graphql.filters import UserFilter + from virtualization.graphql.filters import ClusterFilter + from vpn.graphql.filters import L2VPNFilter, TunnelTerminationFilter + from wireless.graphql.enums import WirelessChannelEnum, WirelessRoleEnum + from wireless.graphql.filters import WirelessLANFilter, WirelessLinkFilter + from .enums import * __all__ = ( 'CableFilter', @@ -13,7 +52,6 @@ __all__ = ( 'DeviceFilter', 'DeviceBayFilter', 'DeviceBayTemplateFilter', - 'InventoryItemTemplateFilter', 'DeviceRoleFilter', 'DeviceTypeFilter', 'FrontPortFilter', @@ -22,6 +60,7 @@ __all__ = ( 'InterfaceTemplateFilter', 'InventoryItemFilter', 'InventoryItemRoleFilter', + 'InventoryItemTemplateFilter', 'LocationFilter', 'MACAddressFilter', 'ManufacturerFilter', @@ -29,6 +68,7 @@ __all__ = ( 'ModuleBayFilter', 'ModuleBayTemplateFilter', 'ModuleTypeFilter', + 'ModuleTypeProfileFilter', 'PlatformFilter', 'PowerFeedFilter', 'PowerOutletFilter', @@ -51,258 +91,872 @@ __all__ = ( @strawberry_django.filter(models.Cable, lookups=True) -@autotype_decorator(filtersets.CableFilterSet) -class CableFilter(BaseFilterMixin): - pass +class CableFilter(PrimaryModelFilterMixin, TenancyFilterMixin): + type: Annotated['CableTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + status: Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + length: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + length_unit: Annotated['CableLengthUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + terminations: Annotated['CableTerminationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.CableTermination, lookups=True) -@autotype_decorator(filtersets.CableTerminationFilterSet) -class CableTerminationFilter(BaseFilterMixin): - pass +class CableTerminationFilter(ChangeLogFilterMixin): + cable: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + cable_id: ID | None = strawberry_django.filter_field() + cable_end: Annotated['CableEndEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type: Annotated['CableTerminationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + termination_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.ConsolePort, lookups=True) -@autotype_decorator(filtersets.ConsolePortFilterSet) -class ConsolePortFilter(BaseFilterMixin): - pass +class ConsolePortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + speed: Annotated['ConsolePortSpeedEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConsolePortTemplate, lookups=True) -@autotype_decorator(filtersets.ConsolePortTemplateFilterSet) -class ConsolePortTemplateFilter(BaseFilterMixin): - pass +class ConsolePortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConsoleServerPort, lookups=True) -@autotype_decorator(filtersets.ConsoleServerPortFilterSet) -class ConsoleServerPortFilter(BaseFilterMixin): - pass +class ConsoleServerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + speed: Annotated['ConsolePortSpeedEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConsoleServerPortTemplate, lookups=True) -@autotype_decorator(filtersets.ConsoleServerPortTemplateFilterSet) -class ConsoleServerPortTemplateFilter(BaseFilterMixin): - pass +class ConsoleServerPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Device, lookups=True) -@autotype_decorator(filtersets.DeviceFilterSet) -class DeviceFilter(BaseFilterMixin): - pass +class DeviceFilter( + ContactFilterMixin, + TenancyFilterMixin, + ImageAttachmentFilterMixin, + RenderConfigFilterMixin, + ConfigContextFilterMixin, + PrimaryModelFilterMixin, +): + device_type: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_type_id: ID | None = strawberry_django.filter_field() + role: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | 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() + position: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + face: Annotated['DeviceFaceEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + status: Annotated['DeviceStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + airflow: Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4_id: ID | None = strawberry_django.filter_field() + primary_ip6: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip6_id: ID | None = strawberry_django.filter_field() + oob_ip: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + oob_ip_id: ID | None = strawberry_django.filter_field() + cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_id: ID | None = strawberry_django.filter_field() + virtual_chassis: Annotated['VirtualChassisFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_chassis_id: ID | None = strawberry_django.filter_field() + vc_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + vc_priority: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + longitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + console_ports: Annotated['ConsolePortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + console_server_ports: Annotated['ConsoleServerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_outlets: Annotated['PowerOutletFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_ports: Annotated['PowerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interfaces: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + front_ports: Annotated['FrontPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_ports: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_bays: Annotated['DeviceBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + module_bays: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + modules: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + console_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + console_server_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_outlet_count: FilterLookup[int] | None = strawberry_django.filter_field() + interface_count: FilterLookup[int] | None = strawberry_django.filter_field() + front_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + rear_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + device_bay_count: FilterLookup[int] | None = strawberry_django.filter_field() + module_bay_count: FilterLookup[int] | None = strawberry_django.filter_field() + inventory_item_count: FilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceBay, lookups=True) -@autotype_decorator(filtersets.DeviceBayFilterSet) -class DeviceBayFilter(BaseFilterMixin): - pass +class DeviceBayFilter(ComponentModelFilterMixin): + installed_device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + installed_device_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceBayTemplate, lookups=True) -@autotype_decorator(filtersets.DeviceBayTemplateFilterSet) -class DeviceBayTemplateFilter(BaseFilterMixin): +class DeviceBayTemplateFilter(ComponentTemplateFilterMixin): pass @strawberry_django.filter(models.InventoryItemTemplate, lookups=True) -@autotype_decorator(filtersets.InventoryItemTemplateFilterSet) -class InventoryItemTemplateFilter(BaseFilterMixin): - pass +class InventoryItemTemplateFilter(ComponentTemplateFilterMixin): + parent: Annotated['InventoryItemTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + component_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + component_id: ID | None = strawberry_django.filter_field() + role: Annotated['InventoryItemRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + part_id: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceRole, lookups=True) -@autotype_decorator(filtersets.DeviceRoleFilterSet) -class DeviceRoleFilter(BaseFilterMixin): - pass +class DeviceRoleFilter(OrganizationalModelFilterMixin, RenderConfigFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + vm_role: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceType, lookups=True) -@autotype_decorator(filtersets.DeviceTypeFilterSet) -class DeviceTypeFilter(BaseFilterMixin): - pass +class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin): + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + default_platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + default_platform_id: ID | None = strawberry_django.filter_field() + part_number: FilterLookup[str] | None = strawberry_django.filter_field() + u_height: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + exclude_from_utilization: FilterLookup[bool] | None = strawberry_django.filter_field() + is_full_depth: FilterLookup[bool] | None = strawberry_django.filter_field() + subdevice_role: Annotated['SubdeviceRoleEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + airflow: Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + front_image: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_image: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + console_port_templates: ( + Annotated['ConsolePortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + console_server_port_templates: ( + Annotated['ConsoleServerPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + power_port_templates: ( + Annotated['PowerPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + power_outlet_templates: ( + Annotated['PowerOutletTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + interface_templates: ( + Annotated['InterfaceTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + front_port_templates: ( + Annotated['FrontPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + rear_port_templates: ( + Annotated['RearPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + device_bay_templates: ( + Annotated['DeviceBayTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + module_bay_templates: ( + Annotated['ModuleBayTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + inventory_item_templates: ( + Annotated['InventoryItemTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + console_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + console_server_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_outlet_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + interface_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + front_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + rear_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + device_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + module_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + inventory_item_template_count: FilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter(models.FrontPort, lookups=True) -@autotype_decorator(filtersets.FrontPortFilterSet) -class FrontPortFilter(BaseFilterMixin): - pass +class FrontPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + rear_port: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_port_id: ID | None = strawberry_django.filter_field() + rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.FrontPortTemplate, lookups=True) -@autotype_decorator(filtersets.FrontPortTemplateFilterSet) -class FrontPortTemplateFilter(BaseFilterMixin): - pass +class FrontPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + rear_port: Annotated['RearPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_port_id: ID | None = strawberry_django.filter_field() + rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.MACAddress, lookups=True) -@autotype_decorator(filtersets.MACAddressFilterSet) -class MACAddressFilter(BaseFilterMixin): - pass +class MACAddressFilter(PrimaryModelFilterMixin): + mac_address: FilterLookup[str] | None = strawberry_django.filter_field() + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Interface, lookups=True) -@autotype_decorator(filtersets.InterfaceFilterSet) -class InterfaceFilter(BaseFilterMixin): - pass +class InterfaceFilter(ModularComponentModelFilterMixin, InterfaceBaseFilterMixin, CabledObjectModelFilterMixin): + vcdcs: Annotated['VirtualDeviceContextFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + lag: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + lag_id: ID | None = strawberry_django.filter_field() + type: Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field() + speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + duplex: Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + wwn: FilterLookup[str] | None = strawberry_django.filter_field() + parent: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + rf_role: Annotated['WirelessRoleEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rf_channel: Annotated['WirelessChannelEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rf_channel_frequency: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + rf_channel_width: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tx_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + poe_type: Annotated['InterfacePoETypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + wireless_link: Annotated['WirelessLinkFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + wireless_link_id: ID | None = strawberry_django.filter_field() + wireless_lans: Annotated['WirelessLANFilter', strawberry.lazy('wireless.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() + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + mac_addresses: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + fhrp_group_assignments: Annotated['FHRPGroupAssignmentFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tunnel_terminations: Annotated['TunnelTerminationFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.InterfaceTemplate, lookups=True) -@autotype_decorator(filtersets.InterfaceTemplateFilterSet) -class InterfaceTemplateFilter(BaseFilterMixin): - pass +class InterfaceTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field() + bridge: Annotated['InterfaceTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + bridge_id: ID | None = strawberry_django.filter_field() + poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + poe_type: Annotated['InterfacePoETypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rf_role: Annotated['WirelessRoleEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.InventoryItem, lookups=True) -@autotype_decorator(filtersets.InventoryItemFilterSet) -class InventoryItemFilter(BaseFilterMixin): - pass +class InventoryItemFilter(ComponentModelFilterMixin): + parent: Annotated['InventoryItemFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + component_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + component_id: ID | None = strawberry_django.filter_field() + status: Annotated['InventoryItemStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['InventoryItemRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + part_id: FilterLookup[str] | None = strawberry_django.filter_field() + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + discovered: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.InventoryItemRole, lookups=True) -@autotype_decorator(filtersets.InventoryItemRoleFilterSet) -class InventoryItemRoleFilter(BaseFilterMixin): - pass +class InventoryItemRoleFilter(OrganizationalModelFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Location, lookups=True) -@autotype_decorator(filtersets.LocationFilterSet) -class LocationFilter(BaseFilterMixin): - pass +class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, NestedGroupModelFilterMixin): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + status: Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + facility: FilterLookup[str] | None = strawberry_django.filter_field() + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Manufacturer, lookups=True) -@autotype_decorator(filtersets.ManufacturerFilterSet) -class ManufacturerFilter(BaseFilterMixin): +class ManufacturerFilter(ContactFilterMixin, OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.Module, lookups=True) -@autotype_decorator(filtersets.ModuleFilterSet) -class ModuleFilter(BaseFilterMixin): - pass +class ModuleFilter(PrimaryModelFilterMixin, ConfigContextFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + module_bay: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + module_bay_id: ID | None = strawberry_django.filter_field() + module_type: Annotated['ModuleTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + module_type_id: ID | None = strawberry_django.filter_field() + status: Annotated['ModuleStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + console_ports: Annotated['ConsolePortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + console_server_ports: Annotated['ConsoleServerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_outlets: Annotated['PowerOutletFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_ports: Annotated['PowerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interfaces: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + front_ports: Annotated['FrontPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_ports: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_bays: Annotated['DeviceBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + module_bays: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + modules: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ModuleBay, lookups=True) -@autotype_decorator(filtersets.ModuleBayFilterSet) -class ModuleBayFilter(BaseFilterMixin): - pass +class ModuleBayFilter(ModularComponentModelFilterMixin): + parent: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + position: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ModuleBayTemplate, lookups=True) -@autotype_decorator(filtersets.ModuleBayTemplateFilterSet) -class ModuleBayTemplateFilter(BaseFilterMixin): - pass +class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin): + position: FilterLookup[str] | None = strawberry_django.filter_field() + + +@strawberry_django.filter(models.ModuleTypeProfile, lookups=True) +class ModuleTypeProfileFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ModuleType, lookups=True) -@autotype_decorator(filtersets.ModuleTypeFilterSet) -class ModuleTypeFilter(BaseFilterMixin): - pass +class ModuleTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin): + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + profile: Annotated['ModuleTypeProfileFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + profile_id: ID | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() + part_number: FilterLookup[str] | None = strawberry_django.filter_field() + airflow: Annotated['ModuleAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + console_port_templates: ( + Annotated['ConsolePortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + console_server_port_templates: ( + Annotated['ConsoleServerPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + power_port_templates: ( + Annotated['PowerPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + power_outlet_templates: ( + Annotated['PowerOutletTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + interface_templates: ( + Annotated['InterfaceTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + front_port_templates: ( + Annotated['FrontPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + rear_port_templates: ( + Annotated['RearPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + device_bay_templates: ( + Annotated['DeviceBayTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + module_bay_templates: ( + Annotated['ModuleBayTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + inventory_item_templates: ( + Annotated['InventoryItemTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() @strawberry_django.filter(models.Platform, lookups=True) -@autotype_decorator(filtersets.PlatformFilterSet) -class PlatformFilter(BaseFilterMixin): - pass +class PlatformFilter(OrganizationalModelFilterMixin): + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + config_template: Annotated['ConfigTemplateFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + config_template_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerFeed, lookups=True) -@autotype_decorator(filtersets.PowerFeedFilterSet) -class PowerFeedFilter(BaseFilterMixin): - pass +class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + power_panel: Annotated['PowerPanelFilter', strawberry.lazy('dcim.graphql.filters')] | 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_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['PowerFeedStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + type: Annotated['PowerFeedTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + supply: Annotated['PowerFeedSupplyEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + phase: Annotated['PowerFeedPhaseEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + voltage: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + amperage: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + max_utilization: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + available_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.PowerOutlet, lookups=True) -@autotype_decorator(filtersets.PowerOutletFilterSet) -class PowerOutletFilter(BaseFilterMixin): - pass +class PowerOutletFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + power_port: Annotated['PowerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_port_id: ID | None = strawberry_django.filter_field() + feed_leg: Annotated['PowerOutletFeedLegEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerOutletTemplate, lookups=True) -@autotype_decorator(filtersets.PowerOutletTemplateFilterSet) -class PowerOutletTemplateFilter(BaseFilterMixin): - pass +class PowerOutletTemplateFilter(ModularComponentModelFilterMixin): + type: Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + power_port: Annotated['PowerPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_port_id: ID | None = strawberry_django.filter_field() + feed_leg: Annotated['PowerOutletFeedLegEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.PowerPanel, lookups=True) -@autotype_decorator(filtersets.PowerPanelFilterSet) -class PowerPanelFilter(BaseFilterMixin): - pass +class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFilterMixin): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerPort, lookups=True) -@autotype_decorator(filtersets.PowerPortFilterSet) -class PowerPortFilter(BaseFilterMixin): - pass +class PowerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + maximum_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + allocated_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.PowerPortTemplate, lookups=True) -@autotype_decorator(filtersets.PowerPortTemplateFilterSet) -class PowerPortTemplateFilter(BaseFilterMixin): - pass +class PowerPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + maximum_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + allocated_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RackType, lookups=True) -@autotype_decorator(filtersets.RackTypeFilterSet) -class RackTypeFilter(BaseFilterMixin): - pass +class RackTypeFilter(RackBaseFilterMixin): + form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Rack, lookups=True) -@autotype_decorator(filtersets.RackFilterSet) -class RackFilter(BaseFilterMixin): - pass +class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, RackBaseFilterMixin): + form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rack_type: Annotated['RackTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rack_type_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + facility_id: FilterLookup[str] | None = strawberry_django.filter_field() + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + status: Annotated['RackStatusEnum', strawberry.lazy('dcim.graphql.enums')] | 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() + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + airflow: Annotated['RackAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RackReservation, lookups=True) -@autotype_decorator(filtersets.RackReservationFilterSet) -class RackReservationFilter(BaseFilterMixin): - pass +class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + rack_id: ID | None = strawberry_django.filter_field() + units: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + user_id: ID | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.RackRole, lookups=True) -@autotype_decorator(filtersets.RackRoleFilterSet) -class RackRoleFilter(BaseFilterMixin): - pass +class RackRoleFilter(OrganizationalModelFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.RearPort, lookups=True) -@autotype_decorator(filtersets.RearPortFilterSet) -class RearPortFilter(BaseFilterMixin): - pass +class RearPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + positions: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RearPortTemplate, lookups=True) -@autotype_decorator(filtersets.RearPortTemplateFilterSet) -class RearPortTemplateFilter(BaseFilterMixin): - pass +class RearPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + positions: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Region, lookups=True) -@autotype_decorator(filtersets.RegionFilterSet) -class RegionFilter(BaseFilterMixin): - pass +class RegionFilter(ContactFilterMixin, NestedGroupModelFilterMixin): + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Site, lookups=True) -@autotype_decorator(filtersets.SiteFilterSet) -class SiteFilter(BaseFilterMixin): - pass +class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + region: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + facility: FilterLookup[str] | 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() + physical_address: FilterLookup[str] | None = strawberry_django.filter_field() + shipping_address: FilterLookup[str] | None = strawberry_django.filter_field() + latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + longitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.SiteGroup, lookups=True) -@autotype_decorator(filtersets.SiteGroupFilterSet) -class SiteGroupFilter(BaseFilterMixin): - pass +class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilterMixin): + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VirtualChassis, lookups=True) -@autotype_decorator(filtersets.VirtualChassisFilterSet) -class VirtualChassisFilter(BaseFilterMixin): - pass +class VirtualChassisFilter(PrimaryModelFilterMixin): + master: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + master_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + domain: FilterLookup[str] | None = strawberry_django.filter_field() + members: ( + Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + member_count: FilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VirtualDeviceContext, lookups=True) -@autotype_decorator(filtersets.VirtualDeviceContextFilterSet) -class VirtualDeviceContextFilter(BaseFilterMixin): - pass +class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + identifier: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4_id: ID | None = strawberry_django.filter_field() + primary_ip6: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip6_id: ID | None = strawberry_django.filter_field() + comments: FilterLookup[str] | None = strawberry_django.filter_field() + interfaces: ( + Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() diff --git a/netbox/dcim/graphql/schema.py b/netbox/dcim/graphql/schema.py index 011a2b58b..1b0661bc2 100644 --- a/netbox/dcim/graphql/schema.py +++ b/netbox/dcim/graphql/schema.py @@ -77,6 +77,9 @@ class DCIMQuery: module_bay_template: ModuleBayTemplateType = strawberry_django.field() module_bay_template_list: List[ModuleBayTemplateType] = strawberry_django.field() + module_type_profile: ModuleTypeProfileType = strawberry_django.field() + module_type_profile_list: List[ModuleTypeProfileType] = strawberry_django.field() + module_type: ModuleTypeType = strawberry_django.field() module_type_list: List[ModuleTypeType] = strawberry_django.field() diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index b644cc50c..7f801c01b 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -6,7 +6,11 @@ import strawberry_django from core.graphql.mixins import ChangelogMixin from dcim import models from extras.graphql.mixins import ( - ConfigContextMixin, ContactsMixin, CustomFieldsMixin, ImageAttachmentsMixin, TagsMixin, + ConfigContextMixin, + ContactsMixin, + CustomFieldsMixin, + ImageAttachmentsMixin, + TagsMixin, ) from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin from netbox.graphql.scalars import BigInt @@ -14,6 +18,23 @@ from netbox.graphql.types import BaseObjectType, NetBoxObjectType, Organizationa from .filters import * from .mixins import CabledObjectMixin, PathEndpointMixin +if TYPE_CHECKING: + from circuits.graphql.types import CircuitTerminationType + from extras.graphql.types import ConfigTemplateType + from ipam.graphql.types import ( + ASNType, + IPAddressType, + PrefixType, + ServiceType, + VLANTranslationPolicyType, + VLANType, + VRFType, + ) + from tenancy.graphql.types import TenantType + from users.graphql.types import UserType + from virtualization.graphql.types import ClusterType, VMInterfaceType, VirtualMachineType + from wireless.graphql.types import WirelessLANType, WirelessLinkType + __all__ = ( 'CableType', 'ComponentType', @@ -40,6 +61,7 @@ __all__ = ( 'ModuleType', 'ModuleBayType', 'ModuleBayTemplateType', + 'ModuleTypeProfileType', 'ModuleTypeType', 'PlatformType', 'PowerFeedType', @@ -111,8 +133,9 @@ class ModularComponentTemplateType(ComponentTemplateType): @strawberry_django.type( models.CableTermination, - exclude=('termination_type', 'termination_id', '_device', '_rack', '_location', '_site'), - filters=CableTerminationFilter + exclude=['termination_type', 'termination_id', '_device', '_rack', '_location', '_site'], + filters=CableTerminationFilter, + pagination=True ) class CableTerminationType(NetBoxObjectType): cable: Annotated["CableType", strawberry.lazy('dcim.graphql.types')] | None @@ -132,7 +155,8 @@ class CableTerminationType(NetBoxObjectType): @strawberry_django.type( models.Cable, fields='__all__', - filters=CableFilter + filters=CableFilter, + pagination=True ) class CableType(NetBoxObjectType): color: str @@ -167,8 +191,9 @@ class CableType(NetBoxObjectType): @strawberry_django.type( models.ConsolePort, - exclude=('_path',), - filters=ConsolePortFilter + exclude=['_path'], + filters=ConsolePortFilter, + pagination=True ) class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): pass @@ -177,7 +202,8 @@ class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin @strawberry_django.type( models.ConsolePortTemplate, fields='__all__', - filters=ConsolePortTemplateFilter + filters=ConsolePortTemplateFilter, + pagination=True ) class ConsolePortTemplateType(ModularComponentTemplateType): pass @@ -185,8 +211,9 @@ class ConsolePortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.ConsoleServerPort, - exclude=('_path',), - filters=ConsoleServerPortFilter + exclude=['_path'], + filters=ConsoleServerPortFilter, + pagination=True ) class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): pass @@ -195,7 +222,8 @@ class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpoin @strawberry_django.type( models.ConsoleServerPortTemplate, fields='__all__', - filters=ConsoleServerPortTemplateFilter + filters=ConsoleServerPortTemplateFilter, + pagination=True ) class ConsoleServerPortTemplateType(ModularComponentTemplateType): pass @@ -204,7 +232,8 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.Device, fields='__all__', - filters=DeviceFilter + filters=DeviceFilter, + pagination=True ) class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): console_port_count: BigInt @@ -259,7 +288,8 @@ class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBo @strawberry_django.type( models.DeviceBay, fields='__all__', - filters=DeviceBayFilter + filters=DeviceBayFilter, + pagination=True ) class DeviceBayType(ComponentType): installed_device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None @@ -268,7 +298,8 @@ class DeviceBayType(ComponentType): @strawberry_django.type( models.DeviceBayTemplate, fields='__all__', - filters=DeviceBayTemplateFilter + filters=DeviceBayTemplateFilter, + pagination=True ) class DeviceBayTemplateType(ComponentTemplateType): pass @@ -276,8 +307,9 @@ class DeviceBayTemplateType(ComponentTemplateType): @strawberry_django.type( models.InventoryItemTemplate, - exclude=('component_type', 'component_id', 'parent'), - filters=InventoryItemTemplateFilter + exclude=['component_type', 'component_id', 'parent'], + filters=InventoryItemTemplateFilter, + pagination=True ) class InventoryItemTemplateType(ComponentTemplateType): role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None @@ -303,9 +335,12 @@ class InventoryItemTemplateType(ComponentTemplateType): @strawberry_django.type( models.DeviceRole, fields='__all__', - filters=DeviceRoleFilter + filters=DeviceRoleFilter, + pagination=True ) class DeviceRoleType(OrganizationalObjectType): + parent: Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')] | None + children: List[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]] color: str config_template: Annotated["ConfigTemplateType", strawberry.lazy('extras.graphql.types')] | None @@ -316,7 +351,8 @@ class DeviceRoleType(OrganizationalObjectType): @strawberry_django.type( models.DeviceType, fields='__all__', - filters=DeviceTypeFilter + filters=DeviceTypeFilter, + pagination=True ) class DeviceTypeType(NetBoxObjectType): console_port_template_count: BigInt @@ -350,7 +386,8 @@ class DeviceTypeType(NetBoxObjectType): @strawberry_django.type( models.FrontPort, fields='__all__', - filters=FrontPortFilter + filters=FrontPortFilter, + pagination=True ) class FrontPortType(ModularComponentType, CabledObjectMixin): color: str @@ -360,7 +397,8 @@ class FrontPortType(ModularComponentType, CabledObjectMixin): @strawberry_django.type( models.FrontPortTemplate, fields='__all__', - filters=FrontPortTemplateFilter + filters=FrontPortTemplateFilter, + pagination=True ) class FrontPortTemplateType(ModularComponentTemplateType): color: str @@ -369,8 +407,9 @@ class FrontPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.MACAddress, - exclude=('assigned_object_type', 'assigned_object_id'), - filters=MACAddressFilter + exclude=['assigned_object_type', 'assigned_object_id'], + filters=MACAddressFilter, + pagination=True ) class MACAddressType(NetBoxObjectType): mac_address: str @@ -385,8 +424,9 @@ class MACAddressType(NetBoxObjectType): @strawberry_django.type( models.Interface, - exclude=('_path',), - filters=InterfaceFilter + exclude=['_path'], + filters=InterfaceFilter, + pagination=True ) class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, PathEndpointMixin): _name: str @@ -413,7 +453,8 @@ class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, P @strawberry_django.type( models.InterfaceTemplate, fields='__all__', - filters=InterfaceTemplateFilter + filters=InterfaceTemplateFilter, + pagination=True ) class InterfaceTemplateType(ModularComponentTemplateType): _name: str @@ -424,8 +465,9 @@ class InterfaceTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.InventoryItem, - exclude=('component_type', 'component_id', 'parent'), - filters=InventoryItemFilter + exclude=['component_type', 'component_id', 'parent'], + filters=InventoryItemFilter, + pagination=True ) class InventoryItemType(ComponentType): role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None @@ -451,7 +493,8 @@ class InventoryItemType(ComponentType): @strawberry_django.type( models.InventoryItemRole, fields='__all__', - filters=InventoryItemRoleFilter + filters=InventoryItemRoleFilter, + pagination=True ) class InventoryItemRoleType(OrganizationalObjectType): color: str @@ -463,8 +506,9 @@ class InventoryItemRoleType(OrganizationalObjectType): @strawberry_django.type( models.Location, # fields='__all__', - exclude=('parent',), # bug - temp - filters=LocationFilter + exclude=['parent'], # bug - temp + filters=LocationFilter, + pagination=True ) class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, OrganizationalObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] @@ -491,7 +535,8 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Organi @strawberry_django.type( models.Manufacturer, fields='__all__', - filters=ManufacturerFilter + filters=ManufacturerFilter, + pagination=True ) class ManufacturerType(OrganizationalObjectType, ContactsMixin): @@ -505,7 +550,8 @@ class ManufacturerType(OrganizationalObjectType, ContactsMixin): @strawberry_django.type( models.Module, fields='__all__', - filters=ModuleFilter + filters=ModuleFilter, + pagination=True ) class ModuleType(NetBoxObjectType): device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] @@ -524,8 +570,9 @@ class ModuleType(NetBoxObjectType): @strawberry_django.type( models.ModuleBay, # fields='__all__', - exclude=('parent',), - filters=ModuleBayFilter + exclude=['parent'], + filters=ModuleBayFilter, + pagination=True ) class ModuleBayType(ModularComponentType): @@ -540,18 +587,31 @@ class ModuleBayType(ModularComponentType): @strawberry_django.type( models.ModuleBayTemplate, fields='__all__', - filters=ModuleBayTemplateFilter + filters=ModuleBayTemplateFilter, + pagination=True ) class ModuleBayTemplateType(ModularComponentTemplateType): pass +@strawberry_django.type( + models.ModuleTypeProfile, + fields='__all__', + filters=ModuleTypeProfileFilter, + pagination=True +) +class ModuleTypeProfileType(NetBoxObjectType): + module_types: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]] + + @strawberry_django.type( models.ModuleType, fields='__all__', - filters=ModuleTypeFilter + filters=ModuleTypeFilter, + pagination=True ) class ModuleTypeType(NetBoxObjectType): + profile: Annotated["ModuleTypeProfileType", strawberry.lazy('dcim.graphql.types')] | None manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] frontporttemplates: List[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]] @@ -567,7 +627,8 @@ class ModuleTypeType(NetBoxObjectType): @strawberry_django.type( models.Platform, fields='__all__', - filters=PlatformFilter + filters=PlatformFilter, + pagination=True ) class PlatformType(OrganizationalObjectType): manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None @@ -579,8 +640,9 @@ class PlatformType(OrganizationalObjectType): @strawberry_django.type( models.PowerFeed, - exclude=('_path',), - filters=PowerFeedFilter + exclude=['_path'], + filters=PowerFeedFilter, + pagination=True ) class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): power_panel: Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')] @@ -590,8 +652,9 @@ class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): @strawberry_django.type( models.PowerOutlet, - exclude=('_path',), - filters=PowerOutletFilter + exclude=['_path'], + filters=PowerOutletFilter, + pagination=True ) class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): power_port: Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')] | None @@ -601,7 +664,8 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin @strawberry_django.type( models.PowerOutletTemplate, fields='__all__', - filters=PowerOutletTemplateFilter + filters=PowerOutletTemplateFilter, + pagination=True ) class PowerOutletTemplateType(ModularComponentTemplateType): power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None @@ -610,7 +674,8 @@ class PowerOutletTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.PowerPanel, fields='__all__', - filters=PowerPanelFilter + filters=PowerPanelFilter, + pagination=True ) class PowerPanelType(NetBoxObjectType, ContactsMixin): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] @@ -621,8 +686,9 @@ class PowerPanelType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.PowerPort, - exclude=('_path',), - filters=PowerPortFilter + exclude=['_path'], + filters=PowerPortFilter, + pagination=True ) class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -632,7 +698,8 @@ class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @strawberry_django.type( models.PowerPortTemplate, fields='__all__', - filters=PowerPortTemplateFilter + filters=PowerPortTemplateFilter, + pagination=True ) class PowerPortTemplateType(ModularComponentTemplateType): poweroutlet_templates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]] @@ -641,7 +708,8 @@ class PowerPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.RackType, fields='__all__', - filters=RackTypeFilter + filters=RackTypeFilter, + pagination=True ) class RackTypeType(NetBoxObjectType): manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] @@ -650,7 +718,8 @@ class RackTypeType(NetBoxObjectType): @strawberry_django.type( models.Rack, fields='__all__', - filters=RackFilter + filters=RackFilter, + pagination=True ) class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] @@ -668,7 +737,8 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje @strawberry_django.type( models.RackReservation, fields='__all__', - filters=RackReservationFilter + filters=RackReservationFilter, + pagination=True ) class RackReservationType(NetBoxObjectType): units: List[int] @@ -680,7 +750,8 @@ class RackReservationType(NetBoxObjectType): @strawberry_django.type( models.RackRole, fields='__all__', - filters=RackRoleFilter + filters=RackRoleFilter, + pagination=True ) class RackRoleType(OrganizationalObjectType): color: str @@ -691,7 +762,8 @@ class RackRoleType(OrganizationalObjectType): @strawberry_django.type( models.RearPort, fields='__all__', - filters=RearPortFilter + filters=RearPortFilter, + pagination=True ) class RearPortType(ModularComponentType, CabledObjectMixin): color: str @@ -702,7 +774,8 @@ class RearPortType(ModularComponentType, CabledObjectMixin): @strawberry_django.type( models.RearPortTemplate, fields='__all__', - filters=RearPortTemplateFilter + filters=RearPortTemplateFilter, + pagination=True ) class RearPortTemplateType(ModularComponentTemplateType): color: str @@ -712,9 +785,9 @@ class RearPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.Region, - exclude=('parent',), - # fields='__all__', - filters=RegionFilter + exclude=['parent'], + filters=RegionFilter, + pagination=True ) class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @@ -739,7 +812,8 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @strawberry_django.type( models.Site, fields='__all__', - filters=SiteFilter + filters=SiteFilter, + pagination=True ) class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): time_zone: str | None @@ -772,9 +846,9 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje @strawberry_django.type( models.SiteGroup, - # fields='__all__', - exclude=('parent',), # bug - temp - filters=SiteGroupFilter + exclude=['parent'], # bug - temp + filters=SiteGroupFilter, + pagination=True ) class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @@ -799,7 +873,8 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @strawberry_django.type( models.VirtualChassis, fields='__all__', - filters=VirtualChassisFilter + filters=VirtualChassisFilter, + pagination=True ) class VirtualChassisType(NetBoxObjectType): member_count: BigInt @@ -811,7 +886,8 @@ class VirtualChassisType(NetBoxObjectType): @strawberry_django.type( models.VirtualDeviceContext, fields='__all__', - filters=VirtualDeviceContextFilter + filters=VirtualDeviceContextFilter, + pagination=True ) class VirtualDeviceContextType(NetBoxObjectType): device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None diff --git a/netbox/dcim/migrations/0002_squashed.py b/netbox/dcim/migrations/0002_squashed.py index 2e830560f..ae1966e58 100644 --- a/netbox/dcim/migrations/0002_squashed.py +++ b/netbox/dcim/migrations/0002_squashed.py @@ -7,11 +7,11 @@ import taggit.managers class Migration(migrations.Migration): dependencies = [ - ('dcim', '0001_initial'), + ('dcim', '0001_squashed'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('contenttypes', '0002_remove_content_type_name'), - ('extras', '0001_initial'), - ('tenancy', '0001_initial'), + ('extras', '0001_squashed'), + ('tenancy', '0001_squashed_0012'), ] replaces = [ diff --git a/netbox/dcim/migrations/0003_squashed_0130.py b/netbox/dcim/migrations/0003_squashed_0130.py index 0248d9ba1..490ab8e8b 100644 --- a/netbox/dcim/migrations/0003_squashed_0130.py +++ b/netbox/dcim/migrations/0003_squashed_0130.py @@ -5,12 +5,12 @@ import taggit.managers class Migration(migrations.Migration): dependencies = [ - ('dcim', '0002_auto_20160622_1821'), - ('virtualization', '0001_virtualization'), + ('dcim', '0002_squashed'), + ('virtualization', '0001_squashed_0022'), ('contenttypes', '0002_remove_content_type_name'), - ('ipam', '0001_initial'), - ('tenancy', '0001_initial'), - ('extras', '0002_custom_fields'), + ('ipam', '0001_squashed'), + ('tenancy', '0001_squashed_0012'), + ('extras', '0002_squashed_0059'), ] replaces = [ @@ -505,28 +505,6 @@ class Migration(migrations.Migration): model_name='cable', name='termination_a_type', field=models.ForeignKey( - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), - models.Q( - ('app_label', 'dcim'), - ( - 'model__in', - ( - 'consoleport', - 'consoleserverport', - 'frontport', - 'interface', - 'powerfeed', - 'poweroutlet', - 'powerport', - 'rearport', - ), - ), - ), - _connector='OR', - ) - ), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype', @@ -536,28 +514,6 @@ class Migration(migrations.Migration): model_name='cable', name='termination_b_type', field=models.ForeignKey( - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), - models.Q( - ('app_label', 'dcim'), - ( - 'model__in', - ( - 'consoleport', - 'consoleserverport', - 'frontport', - 'interface', - 'powerfeed', - 'poweroutlet', - 'powerport', - 'rearport', - ), - ), - ), - _connector='OR', - ) - ), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype', diff --git a/netbox/dcim/migrations/0131_squashed_0159.py b/netbox/dcim/migrations/0131_squashed_0159.py index 3866e8cc8..1c1f2ff38 100644 --- a/netbox/dcim/migrations/0131_squashed_0159.py +++ b/netbox/dcim/migrations/0131_squashed_0159.py @@ -43,12 +43,12 @@ class Migration(migrations.Migration): ] dependencies = [ - ('tenancy', '0012_standardize_models'), + ('tenancy', '0001_squashed_0012'), ('extras', '0002_squashed_0059'), - ('dcim', '0130_sitegroup'), + ('dcim', '0003_squashed_0130'), ('contenttypes', '0002_remove_content_type_name'), - ('ipam', '0053_asn_model'), - ('wireless', '0001_wireless'), + ('ipam', '0047_squashed_0053'), + ('wireless', '0001_squashed_0008'), ] operations = [ @@ -866,21 +866,6 @@ class Migration(migrations.Migration): name='component_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q( - ('app_label', 'dcim'), - ( - 'model__in', - ( - 'consoleport', - 'consoleserverport', - 'frontport', - 'interface', - 'poweroutlet', - 'powerport', - 'rearport', - ), - ), - ), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', @@ -1238,21 +1223,6 @@ class Migration(migrations.Migration): 'component_type', models.ForeignKey( blank=True, - limit_choices_to=models.Q( - ('app_label', 'dcim'), - ( - 'model__in', - ( - 'consoleporttemplate', - 'consoleserverporttemplate', - 'frontporttemplate', - 'interfacetemplate', - 'poweroutlettemplate', - 'powerporttemplate', - 'rearporttemplate', - ), - ), - ), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', @@ -1478,28 +1448,6 @@ class Migration(migrations.Migration): ( 'termination_type', models.ForeignKey( - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'circuits'), ('model__in', ('circuittermination',))), - models.Q( - ('app_label', 'dcim'), - ( - 'model__in', - ( - 'consoleport', - 'consoleserverport', - 'frontport', - 'interface', - 'powerfeed', - 'poweroutlet', - 'powerport', - 'rearport', - ), - ), - ), - _connector='OR', - ) - ), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype', diff --git a/netbox/dcim/migrations/0160_squashed_0166.py b/netbox/dcim/migrations/0160_squashed_0166.py index 0deb58bab..5cff94f4a 100644 --- a/netbox/dcim/migrations/0160_squashed_0166.py +++ b/netbox/dcim/migrations/0160_squashed_0166.py @@ -18,9 +18,9 @@ class Migration(migrations.Migration): dependencies = [ ('ipam', '0047_squashed_0053'), - ('tenancy', '0009_standardize_description_comments'), - ('circuits', '0037_new_cabling_models'), - ('dcim', '0159_populate_cable_paths'), + ('tenancy', '0001_squashed_0012'), + ('circuits', '0003_squashed_0037'), + ('dcim', '0131_squashed_0159'), ] operations = [ diff --git a/netbox/dcim/migrations/0167_squashed_0182.py b/netbox/dcim/migrations/0167_squashed_0182.py index d0ad5379f..ba077ff4e 100644 --- a/netbox/dcim/migrations/0167_squashed_0182.py +++ b/netbox/dcim/migrations/0167_squashed_0182.py @@ -27,10 +27,10 @@ class Migration(migrations.Migration): ] dependencies = [ - ('extras', '0086_configtemplate'), - ('tenancy', '0010_tenant_relax_uniqueness'), + ('extras', '0060_squashed_0086'), + ('tenancy', '0002_squashed_0011'), ('ipam', '0047_squashed_0053'), - ('dcim', '0166_virtualdevicecontext'), + ('dcim', '0160_squashed_0166'), ] operations = [ diff --git a/netbox/dcim/migrations/0183_devicetype_exclude_from_utilization.py b/netbox/dcim/migrations/0183_devicetype_exclude_from_utilization.py index f9f2c20b4..2e3edb08a 100644 --- a/netbox/dcim/migrations/0183_devicetype_exclude_from_utilization.py +++ b/netbox/dcim/migrations/0183_devicetype_exclude_from_utilization.py @@ -5,7 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('dcim', '0182_zero_length_cable_fix'), + ('dcim', '0167_squashed_0182'), ] operations = [ diff --git a/netbox/dcim/migrations/0199_macaddress.py b/netbox/dcim/migrations/0199_macaddress.py index ae18d5f63..c668858b4 100644 --- a/netbox/dcim/migrations/0199_macaddress.py +++ b/netbox/dcim/migrations/0199_macaddress.py @@ -31,13 +31,6 @@ class Migration(migrations.Migration): 'assigned_object_type', models.ForeignKey( blank=True, - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'dcim'), ('model', 'interface')), - models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), - _connector='OR', - ) - ), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/dcim/migrations/0201_add_power_outlet_status.py b/netbox/dcim/migrations/0201_add_power_outlet_status.py new file mode 100644 index 000000000..21fd32186 --- /dev/null +++ b/netbox/dcim/migrations/0201_add_power_outlet_status.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0200_populate_mac_addresses'), + ] + + operations = [ + migrations.AddField( + model_name='poweroutlet', + name='status', + field=models.CharField(default='enabled', max_length=50), + ), + ] diff --git a/netbox/dcim/migrations/0202_location_comments_region_comments_sitegroup_comments.py b/netbox/dcim/migrations/0202_location_comments_region_comments_sitegroup_comments.py new file mode 100644 index 000000000..ffdc5ba8a --- /dev/null +++ b/netbox/dcim/migrations/0202_location_comments_region_comments_sitegroup_comments.py @@ -0,0 +1,26 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0201_add_power_outlet_status'), + ] + + operations = [ + migrations.AddField( + model_name='location', + name='comments', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='region', + name='comments', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='sitegroup', + name='comments', + field=models.TextField(blank=True), + ), + ] diff --git a/netbox/dcim/migrations/0203_add_rack_outer_height.py b/netbox/dcim/migrations/0203_add_rack_outer_height.py new file mode 100644 index 000000000..2d2fef265 --- /dev/null +++ b/netbox/dcim/migrations/0203_add_rack_outer_height.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2b1 on 2025-03-18 15:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0202_location_comments_region_comments_sitegroup_comments'), + ] + + operations = [ + migrations.AddField( + model_name='rack', + name='outer_height', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='racktype', + name='outer_height', + field=models.PositiveSmallIntegerField(blank=True, null=True), + ), + ] diff --git a/netbox/dcim/migrations/0203_device_role_nested.py b/netbox/dcim/migrations/0203_device_role_nested.py new file mode 100644 index 000000000..c9dd791b3 --- /dev/null +++ b/netbox/dcim/migrations/0203_device_role_nested.py @@ -0,0 +1,65 @@ +# Generated by Django 5.1.7 on 2025-03-25 18:06 + +import django.db.models.manager +import mptt.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0203_add_rack_outer_height'), + ] + + operations = [ + migrations.AddField( + model_name='devicerole', + name='level', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='devicerole', + name='lft', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='devicerole', + name='rght', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='devicerole', + name='tree_id', + field=models.PositiveIntegerField(db_index=True, default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='devicerole', + name='parent', + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name='children', + to='dcim.devicerole', + ), + ), + migrations.AddField( + model_name='devicerole', + name='comments', + field=models.TextField(blank=True), + ), + migrations.AlterField( + model_name='devicerole', + name='name', + field=models.CharField(max_length=100), + ), + migrations.AlterField( + model_name='devicerole', + name='slug', + field=models.SlugField(max_length=100), + ), + ] diff --git a/netbox/dcim/migrations/0204_device_role_rebuild.py b/netbox/dcim/migrations/0204_device_role_rebuild.py new file mode 100644 index 000000000..69837c522 --- /dev/null +++ b/netbox/dcim/migrations/0204_device_role_rebuild.py @@ -0,0 +1,22 @@ +from django.db import migrations +import mptt +import mptt.managers + + +def rebuild_mptt(apps, schema_editor): + manager = mptt.managers.TreeManager() + DeviceRole = apps.get_model('dcim', 'DeviceRole') + manager.model = DeviceRole + mptt.register(DeviceRole) + manager.contribute_to_class(DeviceRole, 'objects') + manager.rebuild() + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0203_device_role_nested'), + ] + + operations = [ + migrations.RunPython(code=rebuild_mptt, reverse_code=migrations.RunPython.noop), + ] diff --git a/netbox/dcim/migrations/0205_moduletypeprofile.py b/netbox/dcim/migrations/0205_moduletypeprofile.py new file mode 100644 index 000000000..25ab3415b --- /dev/null +++ b/netbox/dcim/migrations/0205_moduletypeprofile.py @@ -0,0 +1,57 @@ +import django.db.models.deletion +import taggit.managers +from django.db import migrations, models + +import utilities.json + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0204_device_role_rebuild'), + ('extras', '0126_exporttemplate_file_name'), + ] + + operations = [ + migrations.CreateModel( + name='ModuleTypeProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ( + 'custom_field_data', + models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder), + ), + ('description', models.CharField(blank=True, max_length=200)), + ('comments', models.TextField(blank=True)), + ('name', models.CharField(max_length=100, unique=True)), + ('schema', models.JSONField(blank=True, null=True)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'module type profile', + 'verbose_name_plural': 'module type profiles', + 'ordering': ('name',), + }, + ), + migrations.AddField( + model_name='moduletype', + name='attribute_data', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='moduletype', + name='profile', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='module_types', + to='dcim.moduletypeprofile', + ), + ), + migrations.AlterModelOptions( + name='moduletype', + options={'ordering': ('profile', 'manufacturer', 'model')}, + ), + ] diff --git a/netbox/dcim/migrations/0206_load_module_type_profiles.py b/netbox/dcim/migrations/0206_load_module_type_profiles.py new file mode 100644 index 000000000..e3ca7d27a --- /dev/null +++ b/netbox/dcim/migrations/0206_load_module_type_profiles.py @@ -0,0 +1,42 @@ +import json +from pathlib import Path + +from django.db import migrations + +DATA_FILES_PATH = Path(__file__).parent / 'initial_data' / 'module_type_profiles' + + +def load_initial_data(apps, schema_editor): + """ + Load initial ModuleTypeProfile objects from file. + """ + ModuleTypeProfile = apps.get_model('dcim', 'ModuleTypeProfile') + initial_profiles = ( + 'cpu', + 'fan', + 'gpu', + 'hard_disk', + 'memory', + 'power_supply' + ) + + for name in initial_profiles: + file_path = DATA_FILES_PATH / f'{name}.json' + with file_path.open('r') as f: + data = json.load(f) + try: + ModuleTypeProfile.objects.create(**data) + except Exception as e: + print(f"Error loading data from {file_path}") + raise e + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0205_moduletypeprofile'), + ] + + operations = [ + migrations.RunPython(load_initial_data), + ] diff --git a/netbox/dcim/migrations/0207_remove_redundant_indexes.py b/netbox/dcim/migrations/0207_remove_redundant_indexes.py new file mode 100644 index 000000000..b63e6423f --- /dev/null +++ b/netbox/dcim/migrations/0207_remove_redundant_indexes.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2b1 on 2025-04-03 18:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0206_load_module_type_profiles'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='cabletermination', + name='dcim_cablet_termina_884752_idx', + ), + ] diff --git a/netbox/dcim/migrations/initial_data/module_type_profiles/cpu.json b/netbox/dcim/migrations/initial_data/module_type_profiles/cpu.json new file mode 100644 index 000000000..255886c5e --- /dev/null +++ b/netbox/dcim/migrations/initial_data/module_type_profiles/cpu.json @@ -0,0 +1,20 @@ +{ + "name": "CPU", + "schema": { + "properties": { + "architecture": { + "type": "string", + "title": "Architecture" + }, + "speed": { + "type": "number", + "title": "Speed", + "description": "Clock speed in GHz" + }, + "cores": { + "type": "integer", + "description": "Number of cores present" + } + } + } +} diff --git a/netbox/dcim/migrations/initial_data/module_type_profiles/fan.json b/netbox/dcim/migrations/initial_data/module_type_profiles/fan.json new file mode 100644 index 000000000..e6a2a384e --- /dev/null +++ b/netbox/dcim/migrations/initial_data/module_type_profiles/fan.json @@ -0,0 +1,12 @@ +{ + "name": "Fan", + "schema": { + "properties": { + "rpm": { + "type": "integer", + "title": "RPM", + "description": "Fan speed (RPM)" + } + } + } +} diff --git a/netbox/dcim/migrations/initial_data/module_type_profiles/gpu.json b/netbox/dcim/migrations/initial_data/module_type_profiles/gpu.json new file mode 100644 index 000000000..1725a4ab7 --- /dev/null +++ b/netbox/dcim/migrations/initial_data/module_type_profiles/gpu.json @@ -0,0 +1,28 @@ +{ + "name": "GPU", + "schema": { + "properties": { + "interface": { + "type": "string", + "enum": [ + "PCIe 4.0", + "PCIe 4.0 x8", + "PCIe 4.0 x16", + "PCIe 5.0 x16" + ] + }, + "gpu" : { + "type": "string", + "title": "GPU" + }, + "memory": { + "type": "integer", + "title": "Memory (GB)", + "description": "Total memory capacity (in GB)" + } + }, + "required": [ + "memory" + ] + } +} diff --git a/netbox/dcim/migrations/initial_data/module_type_profiles/hard_disk.json b/netbox/dcim/migrations/initial_data/module_type_profiles/hard_disk.json new file mode 100644 index 000000000..8d55cfde6 --- /dev/null +++ b/netbox/dcim/migrations/initial_data/module_type_profiles/hard_disk.json @@ -0,0 +1,29 @@ +{ + "name": "Hard disk", + "schema": { + "properties": { + "type": { + "type": "string", + "title": "Disk type", + "enum": [ + "HD", + "SSD", + "NVME" + ], + "default": "SSD" + }, + "size": { + "type": "integer", + "title": "Size (GB)", + "description": "Raw disk capacity" + }, + "speed": { + "type": "integer", + "title": "Speed (RPM)" + } + }, + "required": [ + "size" + ] + } +} diff --git a/netbox/dcim/migrations/initial_data/module_type_profiles/memory.json b/netbox/dcim/migrations/initial_data/module_type_profiles/memory.json new file mode 100644 index 000000000..8346bfce9 --- /dev/null +++ b/netbox/dcim/migrations/initial_data/module_type_profiles/memory.json @@ -0,0 +1,36 @@ +{ + "name": "Memory", + "schema": { + "properties": { + "class": { + "type": "string", + "title": "Memory class", + "enum": [ + "DDR3", + "DDR4", + "DDR5" + ], + "default": "DDR5" + }, + "size": { + "type": "integer", + "title": "Size (GB)", + "description": "Raw capacity of the module" + }, + "data_rate": { + "type": "integer", + "title": "Data rate", + "description": "Speed in MT/s" + }, + "ecc": { + "type": "boolean", + "title": "ECC", + "description": "Error-correcting code is enabled" + } + }, + "required": [ + "class", + "size" + ] + } +} diff --git a/netbox/dcim/migrations/initial_data/module_type_profiles/power_supply.json b/netbox/dcim/migrations/initial_data/module_type_profiles/power_supply.json new file mode 100644 index 000000000..ea060a889 --- /dev/null +++ b/netbox/dcim/migrations/initial_data/module_type_profiles/power_supply.json @@ -0,0 +1,34 @@ +{ + "name": "Power supply", + "schema": { + "properties": { + "input_current": { + "type": "string", + "title": "Current type", + "enum": [ + "AC", + "DC" + ], + "default": "AC" + }, + "input_voltage": { + "type": "integer", + "title": "Voltage", + "default": 120 + }, + "wattage": { + "type": "integer", + "description": "Available output power (watts)" + }, + "hot_swappable": { + "type": "boolean", + "title": "Hot-swappable", + "default": false + } + }, + "required": [ + "input_current", + "input_voltage" + ] + } +} diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index d74f34828..33af25678 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -2,6 +2,7 @@ from .cables import * from .device_component_templates import * from .device_components import * from .devices import * +from .modules import * from .power import * from .racks import * from .sites import * diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 02658617b..0a28d5acb 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -261,7 +261,6 @@ class CableTermination(ChangeLoggedModel): ) termination_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=CABLE_TERMINATION_MODELS, on_delete=models.PROTECT, related_name='+' ) @@ -301,9 +300,6 @@ class CableTermination(ChangeLoggedModel): class Meta: ordering = ('cable', 'cable_end', 'pk') - indexes = ( - models.Index(fields=('termination_type', 'termination_id')), - ) constraints = ( models.UniqueConstraint( fields=('termination_type', 'termination_id'), diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index b4f057711..e0b05b388 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -751,7 +751,6 @@ class InventoryItemTemplate(MPTTModel, ComponentTemplateModel): ) component_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=MODULAR_COMPONENT_TEMPLATE_MODELS, on_delete=models.PROTECT, related_name='+', blank=True, diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 632121dc2..4b44c5b4e 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -452,6 +452,12 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki """ A physical power outlet (output) within a Device which provides power to a PowerPort. """ + status = models.CharField( + verbose_name=_('status'), + max_length=50, + choices=PowerOutletStatusChoices, + default=PowerOutletStatusChoices.STATUS_ENABLED + ) type = models.CharField( verbose_name=_('type'), max_length=50, @@ -495,6 +501,9 @@ class PowerOutlet(ModularComponentModel, CabledObjectModel, PathEndpoint, Tracki _("Parent power port ({power_port}) must belong to the same device").format(power_port=self.power_port) ) + def get_status_color(self): + return PowerOutletStatusChoices.colors.get(self.status) + # # Interfaces @@ -1268,7 +1277,6 @@ class InventoryItem(MPTTModel, ComponentModel, TrackingModelMixin): ) component_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=MODULAR_COMPONENT_MODELS, on_delete=models.PROTECT, related_name='+', blank=True, diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 78ffe6b66..5988f8241 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -3,7 +3,7 @@ import yaml from functools import cached_property -from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.core.exceptions import ValidationError from django.core.files.storage import default_storage from django.core.validators import MaxValueValidator, MinValueValidator @@ -19,17 +19,19 @@ from core.models import ObjectType from dcim.choices import * from dcim.constants import * from dcim.fields import MACAddressField +from dcim.utils import update_interface_bridges from extras.models import ConfigContextModel, CustomField from extras.querysets import ConfigContextModelQuerySet from netbox.choices import ColorChoices from netbox.config import ConfigItem -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import NestedGroupModel, OrganizationalModel, PrimaryModel from netbox.models.mixins import WeightMixin from netbox.models.features import ContactsMixin, ImageAttachmentsMixin from utilities.fields import ColorField, CounterCacheField from utilities.tracking import TrackingModelMixin from .device_components import * from .mixins import RenderConfigMixin +from .modules import Module __all__ = ( @@ -38,8 +40,6 @@ __all__ = ( 'DeviceType', 'MACAddress', 'Manufacturer', - 'Module', - 'ModuleType', 'Platform', 'VirtualChassis', 'VirtualDeviceContext', @@ -367,108 +367,11 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD -class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): - """ - A ModuleType represents a hardware element that can be installed within a device and which houses additional - components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a - DeviceType, each ModuleType can have console, power, interface, and pass-through port templates assigned to it. It - cannot, however house device bays or module bays. - """ - manufacturer = models.ForeignKey( - to='dcim.Manufacturer', - on_delete=models.PROTECT, - related_name='module_types' - ) - model = models.CharField( - verbose_name=_('model'), - max_length=100 - ) - part_number = models.CharField( - verbose_name=_('part number'), - max_length=50, - blank=True, - help_text=_('Discrete part number (optional)') - ) - airflow = models.CharField( - verbose_name=_('airflow'), - max_length=50, - choices=ModuleAirflowChoices, - blank=True, - null=True - ) - - clone_fields = ('manufacturer', 'weight', 'weight_unit', 'airflow') - prerequisite_models = ( - 'dcim.Manufacturer', - ) - - class Meta: - ordering = ('manufacturer', 'model') - constraints = ( - models.UniqueConstraint( - fields=('manufacturer', 'model'), - name='%(app_label)s_%(class)s_unique_manufacturer_model' - ), - ) - verbose_name = _('module type') - verbose_name_plural = _('module types') - - def __str__(self): - return self.model - - @property - def full_name(self): - return f"{self.manufacturer} {self.model}" - - def to_yaml(self): - data = { - 'manufacturer': self.manufacturer.name, - 'model': self.model, - 'part_number': self.part_number, - 'description': self.description, - 'weight': float(self.weight) if self.weight is not None else None, - 'weight_unit': self.weight_unit, - 'comments': self.comments, - } - - # Component templates - if self.consoleporttemplates.exists(): - data['console-ports'] = [ - c.to_yaml() for c in self.consoleporttemplates.all() - ] - if self.consoleserverporttemplates.exists(): - data['console-server-ports'] = [ - c.to_yaml() for c in self.consoleserverporttemplates.all() - ] - if self.powerporttemplates.exists(): - data['power-ports'] = [ - c.to_yaml() for c in self.powerporttemplates.all() - ] - if self.poweroutlettemplates.exists(): - data['power-outlets'] = [ - c.to_yaml() for c in self.poweroutlettemplates.all() - ] - if self.interfacetemplates.exists(): - data['interfaces'] = [ - c.to_yaml() for c in self.interfacetemplates.all() - ] - if self.frontporttemplates.exists(): - data['front-ports'] = [ - c.to_yaml() for c in self.frontporttemplates.all() - ] - if self.rearporttemplates.exists(): - data['rear-ports'] = [ - c.to_yaml() for c in self.rearporttemplates.all() - ] - - return yaml.dump(dict(data), sort_keys=False) - - # # Devices # -class DeviceRole(OrganizationalModel): +class DeviceRole(NestedGroupModel): """ Devices are organized by functional role; for example, "Core Switch" or "File Server". Each DeviceRole is assigned a color to be used when displaying rack elevations. The vm_role field determines whether the role is applicable to @@ -491,6 +394,8 @@ class DeviceRole(OrganizationalModel): null=True ) + clone_fields = ('parent', 'description') + class Meta: ordering = ('name',) verbose_name = _('device role') @@ -524,23 +429,6 @@ class Platform(OrganizationalModel): verbose_name_plural = _('platforms') -def update_interface_bridges(device, interface_templates, module=None): - """ - Used for device and module instantiation. Iterates all InterfaceTemplates with a bridge assigned - and applies it to the actual interfaces. - """ - for interface_template in interface_templates.exclude(bridge=None): - interface = Interface.objects.get(device=device, name=interface_template.resolve_name(module=module)) - - if interface_template.bridge: - interface.bridge = Interface.objects.get( - device=device, - name=interface_template.bridge.resolve_name(module=module) - ) - interface.full_clean() - interface.save() - - class Device( ContactsMixin, ImageAttachmentsMixin, @@ -721,6 +609,12 @@ class Device( null=True, help_text=_("GPS coordinate in decimal format (xx.yyyyyy)") ) + services = GenericRelation( + to='ipam.Service', + content_type_field='parent_object_type', + object_id_field='parent_object_id', + related_query_name='device', + ) # Counter fields console_port_count = CounterCacheField( @@ -1157,171 +1051,6 @@ class Device( return round(total_weight / 1000, 2) -class Module(PrimaryModel, ConfigContextModel): - """ - A Module represents a field-installable component within a Device which may itself hold multiple device components - (for example, a line card within a chassis switch). Modules are instantiated from ModuleTypes. - """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='modules' - ) - module_bay = models.OneToOneField( - to='dcim.ModuleBay', - on_delete=models.CASCADE, - related_name='installed_module' - ) - module_type = models.ForeignKey( - to='dcim.ModuleType', - on_delete=models.PROTECT, - related_name='instances' - ) - status = models.CharField( - verbose_name=_('status'), - max_length=50, - choices=ModuleStatusChoices, - default=ModuleStatusChoices.STATUS_ACTIVE - ) - serial = models.CharField( - max_length=50, - blank=True, - verbose_name=_('serial number') - ) - asset_tag = models.CharField( - max_length=50, - blank=True, - null=True, - unique=True, - verbose_name=_('asset tag'), - help_text=_('A unique tag used to identify this device') - ) - - clone_fields = ('device', 'module_type', 'status') - - class Meta: - ordering = ('module_bay',) - verbose_name = _('module') - verbose_name_plural = _('modules') - - def __str__(self): - return f'{self.module_bay.name}: {self.module_type} ({self.pk})' - - def get_status_color(self): - return ModuleStatusChoices.colors.get(self.status) - - def clean(self): - super().clean() - - if hasattr(self, "module_bay") and (self.module_bay.device != self.device): - raise ValidationError( - _("Module must be installed within a module bay belonging to the assigned device ({device}).").format( - device=self.device - ) - ) - - # Check for recursion - module = self - module_bays = [] - modules = [] - while module: - if module.pk in modules or module.module_bay.pk in module_bays: - raise ValidationError(_("A module bay cannot belong to a module installed within it.")) - modules.append(module.pk) - module_bays.append(module.module_bay.pk) - module = module.module_bay.module if module.module_bay else None - - def save(self, *args, **kwargs): - is_new = self.pk is None - - super().save(*args, **kwargs) - - adopt_components = getattr(self, '_adopt_components', False) - disable_replication = getattr(self, '_disable_replication', False) - - # We skip adding components if the module is being edited or - # both replication and component adoption is disabled - if not is_new or (disable_replication and not adopt_components): - return - - # Iterate all component types - for templates, component_attribute, component_model in [ - ("consoleporttemplates", "consoleports", ConsolePort), - ("consoleserverporttemplates", "consoleserverports", ConsoleServerPort), - ("interfacetemplates", "interfaces", Interface), - ("powerporttemplates", "powerports", PowerPort), - ("poweroutlettemplates", "poweroutlets", PowerOutlet), - ("rearporttemplates", "rearports", RearPort), - ("frontporttemplates", "frontports", FrontPort), - ("modulebaytemplates", "modulebays", ModuleBay), - ]: - create_instances = [] - update_instances = [] - - # Prefetch installed components - installed_components = { - component.name: component - for component in getattr(self.device, component_attribute).filter(module__isnull=True) - } - - # Get the template for the module type. - for template in getattr(self.module_type, templates).all(): - template_instance = template.instantiate(device=self.device, module=self) - - if adopt_components: - existing_item = installed_components.get(template_instance.name) - - # Check if there's a component with the same name already - if existing_item: - # Assign it to the module - existing_item.module = self - update_instances.append(existing_item) - continue - - # Only create new components if replication is enabled - if not disable_replication: - create_instances.append(template_instance) - - # Set default values for any applicable custom fields - if cf_defaults := CustomField.objects.get_defaults_for_model(component_model): - for component in create_instances: - component.custom_field_data = cf_defaults - - if component_model is not ModuleBay: - component_model.objects.bulk_create(create_instances) - # Emit the post_save signal for each newly created object - for component in create_instances: - post_save.send( - sender=component_model, - instance=component, - created=True, - raw=False, - using='default', - update_fields=None - ) - else: - # ModuleBays must be saved individually for MPTT - for instance in create_instances: - instance.name = instance.name.replace(MODULE_TOKEN, str(self.module_bay.position)) - instance.save() - - update_fields = ['module'] - component_model.objects.bulk_update(update_instances, update_fields) - # Emit the post_save signal for each updated object - for component in update_instances: - post_save.send( - sender=component_model, - instance=component, - created=False, - raw=False, - using='default', - update_fields=update_fields - ) - - # Interface bridges have to be set after interface instantiation - update_interface_bridges(self.device, self.module_type.interfacetemplates, self) - - # # Virtual chassis # @@ -1506,7 +1235,6 @@ class MACAddress(PrimaryModel): ) assigned_object_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=MACADDRESS_ASSIGNMENT_MODELS, on_delete=models.PROTECT, related_name='+', blank=True, diff --git a/netbox/dcim/models/mixins.py b/netbox/dcim/models/mixins.py index a0fc15a25..127dfb9e5 100644 --- a/netbox/dcim/models/mixins.py +++ b/netbox/dcim/models/mixins.py @@ -3,7 +3,6 @@ from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ -from dcim.constants import LOCATION_SCOPE_TYPES __all__ = ( 'CachedScopeMixin', @@ -44,7 +43,6 @@ class CachedScopeMixin(models.Model): scope_type = models.ForeignKey( to='contenttypes.ContentType', on_delete=models.PROTECT, - limit_choices_to=models.Q(model__in=LOCATION_SCOPE_TYPES), related_name='+', blank=True, null=True diff --git a/netbox/dcim/models/modules.py b/netbox/dcim/models/modules.py new file mode 100644 index 000000000..c5830f1db --- /dev/null +++ b/netbox/dcim/models/modules.py @@ -0,0 +1,362 @@ +import jsonschema +import yaml +from django.core.exceptions import ValidationError +from django.db import models +from django.db.models.signals import post_save +from django.utils.translation import gettext_lazy as _ +from jsonschema.exceptions import ValidationError as JSONValidationError + +from dcim.choices import * +from dcim.constants import MODULE_TOKEN +from dcim.utils import update_interface_bridges +from extras.models import ConfigContextModel, CustomField +from netbox.models import PrimaryModel +from netbox.models.features import ImageAttachmentsMixin +from netbox.models.mixins import WeightMixin +from utilities.jsonschema import validate_schema +from utilities.string import title +from .device_components import * + +__all__ = ( + 'Module', + 'ModuleType', + 'ModuleTypeProfile', +) + + +class ModuleTypeProfile(PrimaryModel): + """ + A profile which defines the attributes which can be set on one or more ModuleTypes. + """ + name = models.CharField( + verbose_name=_('name'), + max_length=100, + unique=True + ) + schema = models.JSONField( + blank=True, + null=True, + verbose_name=_('schema') + ) + + clone_fields = ('schema',) + + class Meta: + ordering = ('name',) + verbose_name = _('module type profile') + verbose_name_plural = _('module type profiles') + + def __str__(self): + return self.name + + def clean(self): + super().clean() + + # Validate the schema definition + if self.schema is not None: + try: + validate_schema(self.schema) + except ValidationError as e: + raise ValidationError({ + 'schema': e.message, + }) + + +class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): + """ + A ModuleType represents a hardware element that can be installed within a device and which houses additional + components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a + DeviceType, each ModuleType can have console, power, interface, and pass-through port templates assigned to it. It + cannot, however house device bays or module bays. + """ + profile = models.ForeignKey( + to='dcim.ModuleTypeProfile', + on_delete=models.PROTECT, + related_name='module_types', + blank=True, + null=True + ) + manufacturer = models.ForeignKey( + to='dcim.Manufacturer', + on_delete=models.PROTECT, + related_name='module_types' + ) + model = models.CharField( + verbose_name=_('model'), + max_length=100 + ) + part_number = models.CharField( + verbose_name=_('part number'), + max_length=50, + blank=True, + help_text=_('Discrete part number (optional)') + ) + airflow = models.CharField( + verbose_name=_('airflow'), + max_length=50, + choices=ModuleAirflowChoices, + blank=True, + null=True + ) + attribute_data = models.JSONField( + blank=True, + null=True, + verbose_name=_('attributes') + ) + + clone_fields = ('profile', 'manufacturer', 'weight', 'weight_unit', 'airflow') + prerequisite_models = ( + 'dcim.Manufacturer', + ) + + class Meta: + ordering = ('profile', 'manufacturer', 'model') + constraints = ( + models.UniqueConstraint( + fields=('manufacturer', 'model'), + name='%(app_label)s_%(class)s_unique_manufacturer_model' + ), + ) + verbose_name = _('module type') + verbose_name_plural = _('module types') + + def __str__(self): + return self.model + + @property + def full_name(self): + return f"{self.manufacturer} {self.model}" + + @property + def attributes(self): + """ + Returns a human-friendly representation of the attributes defined for a ModuleType according to its profile. + """ + if not self.attribute_data or self.profile is None or not self.profile.schema: + return {} + attrs = {} + for name, options in self.profile.schema.get('properties', {}).items(): + key = options.get('title', title(name)) + attrs[key] = self.attribute_data.get(name) + return dict(sorted(attrs.items())) + + def clean(self): + super().clean() + + # Validate any attributes against the assigned profile's schema + if self.profile: + try: + jsonschema.validate(self.attribute_data, schema=self.profile.schema) + except JSONValidationError as e: + raise ValidationError(_("Invalid schema: {error}").format(error=e)) + else: + self.attribute_data = None + + def to_yaml(self): + data = { + 'profile': self.profile.name if self.profile else None, + 'manufacturer': self.manufacturer.name, + 'model': self.model, + 'part_number': self.part_number, + 'description': self.description, + 'weight': float(self.weight) if self.weight is not None else None, + 'weight_unit': self.weight_unit, + 'comments': self.comments, + } + + # Component templates + if self.consoleporttemplates.exists(): + data['console-ports'] = [ + c.to_yaml() for c in self.consoleporttemplates.all() + ] + if self.consoleserverporttemplates.exists(): + data['console-server-ports'] = [ + c.to_yaml() for c in self.consoleserverporttemplates.all() + ] + if self.powerporttemplates.exists(): + data['power-ports'] = [ + c.to_yaml() for c in self.powerporttemplates.all() + ] + if self.poweroutlettemplates.exists(): + data['power-outlets'] = [ + c.to_yaml() for c in self.poweroutlettemplates.all() + ] + if self.interfacetemplates.exists(): + data['interfaces'] = [ + c.to_yaml() for c in self.interfacetemplates.all() + ] + if self.frontporttemplates.exists(): + data['front-ports'] = [ + c.to_yaml() for c in self.frontporttemplates.all() + ] + if self.rearporttemplates.exists(): + data['rear-ports'] = [ + c.to_yaml() for c in self.rearporttemplates.all() + ] + + return yaml.dump(dict(data), sort_keys=False) + + +class Module(PrimaryModel, ConfigContextModel): + """ + A Module represents a field-installable component within a Device which may itself hold multiple device components + (for example, a line card within a chassis switch). Modules are instantiated from ModuleTypes. + """ + device = models.ForeignKey( + to='dcim.Device', + on_delete=models.CASCADE, + related_name='modules' + ) + module_bay = models.OneToOneField( + to='dcim.ModuleBay', + on_delete=models.CASCADE, + related_name='installed_module' + ) + module_type = models.ForeignKey( + to='dcim.ModuleType', + on_delete=models.PROTECT, + related_name='instances' + ) + status = models.CharField( + verbose_name=_('status'), + max_length=50, + choices=ModuleStatusChoices, + default=ModuleStatusChoices.STATUS_ACTIVE + ) + serial = models.CharField( + max_length=50, + blank=True, + verbose_name=_('serial number') + ) + asset_tag = models.CharField( + max_length=50, + blank=True, + null=True, + unique=True, + verbose_name=_('asset tag'), + help_text=_('A unique tag used to identify this device') + ) + + clone_fields = ('device', 'module_type', 'status') + + class Meta: + ordering = ('module_bay',) + verbose_name = _('module') + verbose_name_plural = _('modules') + + def __str__(self): + return f'{self.module_bay.name}: {self.module_type} ({self.pk})' + + def get_status_color(self): + return ModuleStatusChoices.colors.get(self.status) + + def clean(self): + super().clean() + + if hasattr(self, "module_bay") and (self.module_bay.device != self.device): + raise ValidationError( + _("Module must be installed within a module bay belonging to the assigned device ({device}).").format( + device=self.device + ) + ) + + # Check for recursion + module = self + module_bays = [] + modules = [] + while module: + if module.pk in modules or module.module_bay.pk in module_bays: + raise ValidationError(_("A module bay cannot belong to a module installed within it.")) + modules.append(module.pk) + module_bays.append(module.module_bay.pk) + module = module.module_bay.module if module.module_bay else None + + def save(self, *args, **kwargs): + is_new = self.pk is None + + super().save(*args, **kwargs) + + adopt_components = getattr(self, '_adopt_components', False) + disable_replication = getattr(self, '_disable_replication', False) + + # We skip adding components if the module is being edited or + # both replication and component adoption is disabled + if not is_new or (disable_replication and not adopt_components): + return + + # Iterate all component types + for templates, component_attribute, component_model in [ + ("consoleporttemplates", "consoleports", ConsolePort), + ("consoleserverporttemplates", "consoleserverports", ConsoleServerPort), + ("interfacetemplates", "interfaces", Interface), + ("powerporttemplates", "powerports", PowerPort), + ("poweroutlettemplates", "poweroutlets", PowerOutlet), + ("rearporttemplates", "rearports", RearPort), + ("frontporttemplates", "frontports", FrontPort), + ("modulebaytemplates", "modulebays", ModuleBay), + ]: + create_instances = [] + update_instances = [] + + # Prefetch installed components + installed_components = { + component.name: component + for component in getattr(self.device, component_attribute).filter(module__isnull=True) + } + + # Get the template for the module type. + for template in getattr(self.module_type, templates).all(): + template_instance = template.instantiate(device=self.device, module=self) + + if adopt_components: + existing_item = installed_components.get(template_instance.name) + + # Check if there's a component with the same name already + if existing_item: + # Assign it to the module + existing_item.module = self + update_instances.append(existing_item) + continue + + # Only create new components if replication is enabled + if not disable_replication: + create_instances.append(template_instance) + + # Set default values for any applicable custom fields + if cf_defaults := CustomField.objects.get_defaults_for_model(component_model): + for component in create_instances: + component.custom_field_data = cf_defaults + + if component_model is not ModuleBay: + component_model.objects.bulk_create(create_instances) + # Emit the post_save signal for each newly created object + for component in create_instances: + post_save.send( + sender=component_model, + instance=component, + created=True, + raw=False, + using='default', + update_fields=None + ) + else: + # ModuleBays must be saved individually for MPTT + for instance in create_instances: + instance.name = instance.name.replace(MODULE_TOKEN, str(self.module_bay.position)) + instance.save() + + update_fields = ['module'] + component_model.objects.bulk_update(update_instances, update_fields) + # Emit the post_save signal for each updated object + for component in update_instances: + post_save.send( + sender=component_model, + instance=component, + created=False, + raw=False, + using='default', + update_fields=update_fields + ) + + # Interface bridges have to be set after interface instantiation + update_interface_bridges(self.device, self.module_type.interfacetemplates, self) diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index aee142e01..b15cd8b34 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -73,6 +73,12 @@ class RackBase(WeightMixin, PrimaryModel): null=True, help_text=_('Outer dimension of rack (width)') ) + outer_height = models.PositiveSmallIntegerField( + verbose_name=_('outer height'), + blank=True, + null=True, + help_text=_('Outer dimension of rack (height)') + ) outer_depth = models.PositiveSmallIntegerField( verbose_name=_('outer depth'), blank=True, @@ -140,7 +146,7 @@ class RackType(RackBase): ) clone_fields = ( - 'manufacturer', 'form_factor', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', + 'manufacturer', 'form_factor', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', ) prerequisite_models = ( @@ -173,8 +179,8 @@ class RackType(RackBase): super().clean() # Validate outer dimensions and unit - if (self.outer_width is not None or self.outer_depth is not None) and not self.outer_unit: - raise ValidationError(_("Must specify a unit when setting an outer width/depth")) + if any([self.outer_width, self.outer_depth, self.outer_height]) and not self.outer_unit: + raise ValidationError(_("Must specify a unit when setting an outer dimension")) # Validate max_weight and weight_unit if self.max_weight and not self.weight_unit: @@ -188,7 +194,7 @@ class RackType(RackBase): self._abs_max_weight = None # Clear unit if outer width & depth are not set - if self.outer_width is None and self.outer_depth is None: + if not any([self.outer_width, self.outer_depth, self.outer_height]): self.outer_unit = None super().save(*args, **kwargs) @@ -235,8 +241,8 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): """ # Fields which cannot be set locally if a RackType is assigned RACKTYPE_FIELDS = ( - 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_depth', - 'outer_unit', 'mounting_depth', 'weight', 'weight_unit', 'max_weight', + 'form_factor', 'width', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height', + 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'weight_unit', 'max_weight', ) form_factor = models.CharField( @@ -329,7 +335,8 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): clone_fields = ( 'site', 'location', 'tenant', 'status', 'role', 'form_factor', 'width', 'airflow', 'u_height', 'desc_units', - 'outer_width', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', + 'outer_width', 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', + 'weight_unit', ) prerequisite_models = ( 'dcim.Site', @@ -364,8 +371,8 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): raise ValidationError(_("Assigned location must belong to parent site ({site}).").format(site=self.site)) # Validate outer dimensions and unit - if (self.outer_width is not None or self.outer_depth is not None) and not self.outer_unit: - raise ValidationError(_("Must specify a unit when setting an outer width/depth")) + if any([self.outer_width, self.outer_depth, self.outer_height]) and not self.outer_unit: + raise ValidationError(_("Must specify a unit when setting an outer dimension")) # Validate max_weight and weight_unit if self.max_weight and not self.weight_unit: @@ -414,7 +421,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase): self._abs_max_weight = None # Clear unit if outer width & depth are not set - if self.outer_width is None and self.outer_depth is None: + if not any([self.outer_width, self.outer_depth, self.outer_height]): self.outer_unit = None super().save(*args, **kwargs) diff --git a/netbox/dcim/search.py b/netbox/dcim/search.py index 964880990..8ef6a1d44 100644 --- a/netbox/dcim/search.py +++ b/netbox/dcim/search.py @@ -145,6 +145,7 @@ class LocationIndex(SearchIndex): ('facility', 100), ('slug', 110), ('description', 500), + ('comments', 5000), ) display_attrs = ('site', 'status', 'tenant', 'facility', 'description') @@ -183,6 +184,17 @@ class ModuleBayIndex(SearchIndex): display_attrs = ('device', 'label', 'position', 'description') +@register_search +class ModuleTypeProfileIndex(SearchIndex): + model = models.ModuleTypeProfile + fields = ( + ('name', 100), + ('description', 500), + ('comments', 5000), + ) + display_attrs = ('name', 'description') + + @register_search class ModuleTypeIndex(SearchIndex): model = models.ModuleType @@ -225,7 +237,7 @@ class PowerOutletIndex(SearchIndex): ('label', 200), ('description', 500), ) - display_attrs = ('device', 'label', 'type', 'description') + display_attrs = ('device', 'label', 'type', 'status', 'description') @register_search @@ -318,6 +330,7 @@ class RegionIndex(SearchIndex): ('name', 100), ('slug', 110), ('description', 500), + ('comments', 5000), ) display_attrs = ('parent', 'description') @@ -344,6 +357,7 @@ class SiteGroupIndex(SearchIndex): ('name', 100), ('slug', 110), ('description', 500), + ('comments', 5000), ) display_attrs = ('parent', 'description') diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 5320820cd..d58e4e376 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -59,7 +59,7 @@ MACADDRESS_COPY_BUTTON = """ # class DeviceRoleTable(NetBoxTable): - name = tables.Column( + name = columns.MPTTColumn( verbose_name=_('Name'), linkify=True ) @@ -520,6 +520,9 @@ class PowerOutletTable(ModularDeviceComponentTable, PathEndpointTable): verbose_name=_('Power Port'), linkify=True ) + status = columns.ChoiceFieldColumn( + verbose_name=_('Status'), + ) color = columns.ColorColumn() tags = columns.TagColumn( url_name='dcim:poweroutlet_list' @@ -530,9 +533,11 @@ class PowerOutletTable(ModularDeviceComponentTable, PathEndpointTable): fields = ( 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'description', 'power_port', 'color', 'feed_leg', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'inventory_items', - 'tags', 'created', 'last_updated', + 'tags', 'created', 'last_updated', 'status', + ) + default_columns = ( + 'pk', 'name', 'device', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'description', ) - default_columns = ('pk', 'name', 'device', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description') class DevicePowerOutletTable(PowerOutletTable): @@ -550,9 +555,11 @@ class DevicePowerOutletTable(PowerOutletTable): fields = ( 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', 'connection', 'tags', 'actions', + 'status', ) default_columns = ( - 'pk', 'name', 'label', 'type', 'color', 'power_port', 'feed_leg', 'description', 'cable', 'connection', + 'pk', 'name', 'label', 'type', 'status', 'color', 'power_port', 'feed_leg', 'description', 'cable', + 'connection', ) diff --git a/netbox/dcim/tables/modules.py b/netbox/dcim/tables/modules.py index 6bd0d53b5..52edea8b4 100644 --- a/netbox/dcim/tables/modules.py +++ b/netbox/dcim/tables/modules.py @@ -1,25 +1,64 @@ from django.utils.translation import gettext_lazy as _ import django_tables2 as tables -from dcim.models import Module, ModuleType +from dcim.models import Module, ModuleType, ModuleTypeProfile from netbox.tables import NetBoxTable, columns -from .template_code import WEIGHT +from .template_code import MODULETYPEPROFILE_ATTRIBUTES, WEIGHT __all__ = ( 'ModuleTable', + 'ModuleTypeProfileTable', 'ModuleTypeTable', ) +class ModuleTypeProfileTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True + ) + attributes = columns.TemplateColumn( + template_code=MODULETYPEPROFILE_ATTRIBUTES, + accessor=tables.A('schema__properties'), + orderable=False, + verbose_name=_('Attributes') + ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments'), + ) + tags = columns.TagColumn( + url_name='dcim:moduletypeprofile_list' + ) + + class Meta(NetBoxTable.Meta): + model = ModuleTypeProfile + fields = ( + 'pk', 'id', 'name', 'description', 'comments', 'tags', 'created', 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'description', 'attributes', + ) + + class ModuleTypeTable(NetBoxTable): - model = tables.Column( - linkify=True, - verbose_name=_('Module Type') + profile = tables.Column( + verbose_name=_('Profile'), + linkify=True ) manufacturer = tables.Column( verbose_name=_('Manufacturer'), linkify=True ) + model = tables.Column( + linkify=True, + verbose_name=_('Module Type') + ) + weight = columns.TemplateColumn( + verbose_name=_('Weight'), + template_code=WEIGHT, + order_by=('_abs_weight', 'weight_unit') + ) + attributes = columns.DictColumn() instance_count = columns.LinkedCountColumn( viewname='dcim:module_list', url_params={'module_type_id': 'pk'}, @@ -31,20 +70,15 @@ class ModuleTypeTable(NetBoxTable): tags = columns.TagColumn( url_name='dcim:moduletype_list' ) - weight = columns.TemplateColumn( - verbose_name=_('Weight'), - template_code=WEIGHT, - order_by=('_abs_weight', 'weight_unit') - ) class Meta(NetBoxTable.Meta): model = ModuleType fields = ( - 'pk', 'id', 'model', 'manufacturer', 'part_number', 'airflow', 'weight', 'description', 'comments', 'tags', - 'created', 'last_updated', + 'pk', 'id', 'model', 'profile', 'manufacturer', 'part_number', 'airflow', 'weight', 'description', + 'attributes', 'comments', 'tags', 'created', 'last_updated', ) default_columns = ( - 'pk', 'model', 'manufacturer', 'part_number', + 'pk', 'model', 'profile', 'manufacturer', 'part_number', ) diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index dbd99ca24..ee40056de 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -5,7 +5,7 @@ from django_tables2.utils import Accessor from dcim.models import Rack, RackReservation, RackRole, RackType from netbox.tables import NetBoxTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin -from .template_code import WEIGHT +from .template_code import OUTER_UNIT, WEIGHT __all__ = ( 'RackTable', @@ -62,12 +62,16 @@ class RackTypeTable(NetBoxTable): template_code="{{ value }}U", verbose_name=_('Height') ) - outer_width = tables.TemplateColumn( - template_code="{{ record.outer_width }} {{ record.outer_unit }}", + outer_width = columns.TemplateColumn( + template_code=OUTER_UNIT, verbose_name=_('Outer Width') ) - outer_depth = tables.TemplateColumn( - template_code="{{ record.outer_depth }} {{ record.outer_unit }}", + outer_height = columns.TemplateColumn( + template_code=OUTER_UNIT, + verbose_name=_('Outer Height') + ) + outer_depth = columns.TemplateColumn( + template_code=OUTER_UNIT, verbose_name=_('Outer Depth') ) weight = columns.TemplateColumn( @@ -96,8 +100,8 @@ class RackTypeTable(NetBoxTable): model = RackType fields = ( 'pk', 'id', 'model', 'manufacturer', 'form_factor', 'u_height', 'starting_unit', 'width', 'outer_width', - 'outer_depth', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'description', 'comments', - 'instance_count', 'tags', 'created', 'last_updated', + 'outer_height', 'outer_depth', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'description', + 'comments', 'instance_count', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'model', 'manufacturer', 'type', 'u_height', 'description', 'instance_count', @@ -159,12 +163,16 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): tags = columns.TagColumn( url_name='dcim:rack_list' ) - outer_width = tables.TemplateColumn( - template_code="{{ record.outer_width }} {{ record.outer_unit }}", + outer_width = columns.TemplateColumn( + template_code=OUTER_UNIT, verbose_name=_('Outer Width') ) - outer_depth = tables.TemplateColumn( - template_code="{{ record.outer_depth }} {{ record.outer_unit }}", + outer_height = columns.TemplateColumn( + template_code=OUTER_UNIT, + verbose_name=_('Outer Height') + ) + outer_depth = columns.TemplateColumn( + template_code=OUTER_UNIT, verbose_name=_('Outer Depth') ) weight = columns.TemplateColumn( @@ -183,8 +191,9 @@ class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): fields = ( 'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'tenant_group', 'role', 'rack_type', 'serial', 'asset_tag', 'form_factor', 'u_height', 'starting_unit', 'width', 'outer_width', - 'outer_depth', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'comments', 'device_count', - 'get_utilization', 'get_power_utilization', 'description', 'contacts', 'tags', 'created', 'last_updated', + 'outer_height', 'outer_depth', 'mounting_depth', 'airflow', 'weight', 'max_weight', 'comments', + 'device_count', 'get_utilization', 'get_power_utilization', 'description', 'contacts', + 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'rack_type', 'u_height', diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py index 5206bb755..0c9494d51 100644 --- a/netbox/dcim/tables/sites.py +++ b/netbox/dcim/tables/sites.py @@ -32,12 +32,15 @@ class RegionTable(ContactsColumnMixin, NetBoxTable): tags = columns.TagColumn( url_name='dcim:region_list' ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments'), + ) class Meta(NetBoxTable.Meta): model = Region fields = ( - 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'created', 'last_updated', - 'actions', + 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags', + 'created', 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'site_count', 'description') @@ -59,12 +62,15 @@ class SiteGroupTable(ContactsColumnMixin, NetBoxTable): tags = columns.TagColumn( url_name='dcim:sitegroup_list' ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments'), + ) class Meta(NetBoxTable.Meta): model = SiteGroup fields = ( - 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'created', 'last_updated', - 'actions', + 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags', + 'created', 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'site_count', 'description') @@ -157,12 +163,16 @@ class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): actions = columns.ActionsColumn( extra_buttons=LOCATION_BUTTONS ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments'), + ) class Meta(NetBoxTable.Meta): model = Location fields = ( 'pk', 'id', 'name', 'site', 'status', 'facility', 'tenant', 'tenant_group', 'rack_count', 'device_count', - 'description', 'slug', 'contacts', 'tags', 'actions', 'created', 'last_updated', 'vlangroup_count', + 'description', 'slug', 'comments', 'contacts', 'tags', 'actions', 'created', 'last_updated', + 'vlangroup_count', ) default_columns = ( 'pk', 'name', 'site', 'status', 'facility', 'tenant', 'rack_count', 'device_count', 'vlangroup_count', diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index 3b2a9b4c3..356f76750 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -109,6 +109,11 @@ LOCATION_BUTTONS = """ """ +OUTER_UNIT = """ +{% load helpers %} +{% if value %}{{ value }} {{ record.outer_unit }}{% endif %} +""" + # # Device component templatebuttons # @@ -563,3 +568,7 @@ MODULEBAY_BUTTONS = """ {% endif %} {% endif %} """ + +MODULETYPEPROFILE_ATTRIBUTES = """ +{% if value %}{% for attr in value %}{{ attr }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %} +""" diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index b1ed4aca3..c3ac6053d 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -74,6 +74,7 @@ class RegionTest(APIViewTestCases.APIViewTestCase): { 'name': 'Region 4', 'slug': 'region-4', + 'comments': 'this is region 4, not region 5', }, { 'name': 'Region 5', @@ -86,13 +87,14 @@ class RegionTest(APIViewTestCases.APIViewTestCase): ] bulk_update_data = { 'description': 'New description', + 'comments': 'New comments', } @classmethod def setUpTestData(cls): Region.objects.create(name='Region 1', slug='region-1') - Region.objects.create(name='Region 2', slug='region-2') + Region.objects.create(name='Region 2', slug='region-2', comments='what in the world is happening?') Region.objects.create(name='Region 3', slug='region-3') @@ -103,26 +105,30 @@ class SiteGroupTest(APIViewTestCases.APIViewTestCase): { 'name': 'Site Group 4', 'slug': 'site-group-4', + 'comments': '', }, { 'name': 'Site Group 5', 'slug': 'site-group-5', + 'comments': 'not actually empty', }, { 'name': 'Site Group 6', 'slug': 'site-group-6', + 'comments': 'Do I really exist?', }, ] bulk_update_data = { 'description': 'New description', + 'comments': 'I do exist!', } @classmethod def setUpTestData(cls): SiteGroup.objects.create(name='Site Group 1', slug='site-group-1') - SiteGroup.objects.create(name='Site Group 2', slug='site-group-2') - SiteGroup.objects.create(name='Site Group 3', slug='site-group-3') + SiteGroup.objects.create(name='Site Group 2', slug='site-group-2', comments='') + SiteGroup.objects.create(name='Site Group 3', slug='site-group-3', comments='Hi!') class SiteTest(APIViewTestCases.APIViewTestCase): @@ -212,12 +218,14 @@ class LocationTest(APIViewTestCases.APIViewTestCase): name='Parent Location 1', slug='parent-location-1', status=LocationStatusChoices.STATUS_ACTIVE, + comments='First!' ), Location.objects.create( site=sites[1], name='Parent Location 2', slug='parent-location-2', status=LocationStatusChoices.STATUS_ACTIVE, + comments='Second!' ), ) @@ -227,6 +235,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase): slug='location-1', parent=parent_locations[0], status=LocationStatusChoices.STATUS_ACTIVE, + comments='Third!' ) Location.objects.create( site=sites[0], @@ -250,6 +259,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase): 'site': sites[1].pk, 'parent': parent_locations[1].pk, 'status': LocationStatusChoices.STATUS_PLANNED, + 'comments': '', }, { 'name': 'Test Location 5', @@ -257,6 +267,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase): 'site': sites[1].pk, 'parent': parent_locations[1].pk, 'status': LocationStatusChoices.STATUS_PLANNED, + 'comments': 'Somebody should check on this location', }, { 'name': 'Test Location 6', @@ -580,7 +591,7 @@ class DeviceTypeTest(APIViewTestCases.APIViewTestCase): class ModuleTypeTest(APIViewTestCases.APIViewTestCase): model = ModuleType - brief_fields = ['description', 'display', 'id', 'manufacturer', 'model', 'url'] + brief_fields = ['description', 'display', 'id', 'manufacturer', 'model', 'profile', 'url'] bulk_update_data = { 'part_number': 'ABC123', } @@ -618,6 +629,70 @@ class ModuleTypeTest(APIViewTestCases.APIViewTestCase): ] +class ModuleTypeProfileTest(APIViewTestCases.APIViewTestCase): + model = ModuleTypeProfile + brief_fields = ['description', 'display', 'id', 'name', 'url'] + SCHEMAS = [ + { + "properties": { + "foo": { + "type": "string" + } + } + }, + { + "properties": { + "foo": { + "type": "integer" + } + } + }, + { + "properties": { + "foo": { + "type": "boolean" + } + } + }, + ] + create_data = [ + { + 'name': 'Module Type Profile 4', + 'schema': SCHEMAS[0], + }, + { + 'name': 'Module Type Profile 5', + 'schema': SCHEMAS[1], + }, + { + 'name': 'Module Type Profile 6', + 'schema': SCHEMAS[2], + }, + ] + bulk_update_data = { + 'description': 'New description', + 'comments': 'New comments', + } + + @classmethod + def setUpTestData(cls): + module_type_profiles = ( + ModuleTypeProfile( + name='Module Type Profile 1', + schema=cls.SCHEMAS[0] + ), + ModuleTypeProfile( + name='Module Type Profile 2', + schema=cls.SCHEMAS[1] + ), + ModuleTypeProfile( + name='Module Type Profile 3', + schema=cls.SCHEMAS[2] + ), + ) + ModuleTypeProfile.objects.bulk_create(module_type_profiles) + + class ConsolePortTemplateTest(APIViewTestCases.APIViewTestCase): model = ConsolePortTemplate brief_fields = ['description', 'display', 'id', 'name', 'url'] @@ -1138,7 +1213,9 @@ class InventoryItemTemplateTest(APIViewTestCases.APIViewTestCase): class DeviceRoleTest(APIViewTestCases.APIViewTestCase): model = DeviceRole - brief_fields = ['description', 'device_count', 'display', 'id', 'name', 'slug', 'url', 'virtualmachine_count'] + brief_fields = [ + '_depth', 'description', 'device_count', 'display', 'id', 'name', 'slug', 'url', 'virtualmachine_count' + ] create_data = [ { 'name': 'Device Role 4', @@ -1163,12 +1240,9 @@ class DeviceRoleTest(APIViewTestCases.APIViewTestCase): @classmethod def setUpTestData(cls): - roles = ( - DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000'), - DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00'), - DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff'), - ) - DeviceRole.objects.bulk_create(roles) + DeviceRole.objects.create(name='Device Role 1', slug='device-role-1', color='ff0000') + DeviceRole.objects.create(name='Device Role 2', slug='device-role-2', color='00ff00') + DeviceRole.objects.create(name='Device Role 3', slug='device-role-3', color='0000ff') class PlatformTest(APIViewTestCases.APIViewTestCase): @@ -1241,7 +1315,8 @@ class DeviceTest(APIViewTestCases.APIViewTestCase): DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000'), DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() cluster_type = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1') diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 3fa44927d..ba8d4203d 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -67,9 +67,15 @@ class RegionTestCase(TestCase, ChangeLoggedFilterSetTests): def setUpTestData(cls): parent_regions = ( - Region(name='Region 1', slug='region-1', description='foobar1'), - Region(name='Region 2', slug='region-2', description='foobar2'), - Region(name='Region 3', slug='region-3', description='foobar3'), + Region( + name='Region 1', slug='region-1', description='foobar1', comments="There's nothing that", + ), + Region( + name='Region 2', slug='region-2', description='foobar2', comments='a hundred men or more', + ), + Region( + name='Region 3', slug='region-3', description='foobar3', comments='could ever do' + ), ) for region in parent_regions: region.save() @@ -100,6 +106,13 @@ class RegionTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_q_comments(self): + params = {'q': 'there'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'q': 'hundred men could'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + def test_name(self): params = {'name': ['Region 1', 'Region 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -148,13 +161,17 @@ class SiteGroupTestCase(TestCase, ChangeLoggedFilterSetTests): SiteGroup(name='Site Group 2A', slug='site-group-2a', parent=parent_groups[1]), SiteGroup(name='Site Group 2B', slug='site-group-2b', parent=parent_groups[1]), SiteGroup(name='Site Group 3A', slug='site-group-3a', parent=parent_groups[2]), - SiteGroup(name='Site Group 3B', slug='site-group-3b', parent=parent_groups[2]), + SiteGroup( + name='Site Group 3B', slug='site-group-3b', parent=parent_groups[2], comments='this is a parent group', + ), ) for site_group in groups: site_group.save() child_groups = ( - SiteGroup(name='Site Group 1A1', slug='site-group-1a1', parent=groups[0]), + SiteGroup( + name='Site Group 1A1', slug='site-group-1a1', parent=groups[0], comments='this is a child group', + ), SiteGroup(name='Site Group 1B1', slug='site-group-1b1', parent=groups[1]), SiteGroup(name='Site Group 2A1', slug='site-group-2a1', parent=groups[2]), SiteGroup(name='Site Group 2B1', slug='site-group-2b1', parent=groups[3]), @@ -168,6 +185,13 @@ class SiteGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_q_comments(self): + params = {'q': 'this'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + params = {'q': 'child'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_name(self): params = {'name': ['Site Group 1', 'Site Group 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -401,6 +425,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests): status=LocationStatusChoices.STATUS_PLANNED, facility='Facility 1', description='foobar1', + comments='', ), Location( name='Location 2A', @@ -410,6 +435,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests): status=LocationStatusChoices.STATUS_STAGING, facility='Facility 2', description='foobar2', + comments='First comment!', ), Location( name='Location 3A', @@ -419,6 +445,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests): status=LocationStatusChoices.STATUS_DECOMMISSIONING, facility='Facility 3', description='foobar3', + comments='_This_ is a **bold comment**', ), ) for location in locations: @@ -436,6 +463,13 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_q_comments(self): + params = {'q': 'this'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'q': 'comment'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_name(self): params = {'name': ['Location 1', 'Location 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -551,6 +585,7 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests): starting_unit=1, desc_units=False, outer_width=100, + outer_height=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, mounting_depth=100, @@ -569,6 +604,7 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests): starting_unit=2, desc_units=False, outer_width=200, + outer_height=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, mounting_depth=200, @@ -587,6 +623,7 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests): starting_unit=3, desc_units=True, outer_width=300, + outer_height=300, outer_depth=300, outer_unit=RackDimensionUnitChoices.UNIT_INCH, mounting_depth=300, @@ -647,6 +684,10 @@ class RackTypeTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'outer_width': [100, 200]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_outer_height(self): + params = {'outer_height': [100, 200]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_outer_depth(self): params = {'outer_depth': [100, 200]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -730,6 +771,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): starting_unit=1, desc_units=False, outer_width=100, + outer_height=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, mounting_depth=100, @@ -748,6 +790,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): starting_unit=2, desc_units=False, outer_width=200, + outer_height=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, mounting_depth=200, @@ -797,6 +840,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): u_height=42, desc_units=False, outer_width=100, + outer_height=100, outer_depth=100, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, weight=10, @@ -820,6 +864,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): u_height=43, desc_units=False, outer_width=200, + outer_height=200, outer_depth=200, outer_unit=RackDimensionUnitChoices.UNIT_MILLIMETER, weight=20, @@ -843,6 +888,7 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): u_height=44, desc_units=True, outer_width=300, + outer_height=300, outer_depth=300, outer_unit=RackDimensionUnitChoices.UNIT_INCH, weight=30, @@ -923,6 +969,10 @@ class RackTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'outer_width': [100, 200]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_outer_height(self): + params = {'outer_height': [100, 200]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_outer_depth(self): params = {'outer_depth': [100, 200]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -1436,6 +1486,16 @@ class DeviceTypeTestCase(TestCase, ChangeLoggedFilterSetTests): class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = ModuleType.objects.all() filterset = ModuleTypeFilterSet + ignore_fields = ['attribute_data'] + + PROFILE_SCHEMA = { + "properties": { + "string": {"type": "string"}, + "integer": {"type": "integer"}, + "number": {"type": "number"}, + "boolean": {"type": "boolean"}, + } + } @classmethod def setUpTestData(cls): @@ -1446,6 +1506,21 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): Manufacturer(name='Manufacturer 3', slug='manufacturer-3'), ) Manufacturer.objects.bulk_create(manufacturers) + module_type_profiles = ( + ModuleTypeProfile( + name='Module Type Profile 1', + schema=cls.PROFILE_SCHEMA + ), + ModuleTypeProfile( + name='Module Type Profile 2', + schema=cls.PROFILE_SCHEMA + ), + ModuleTypeProfile( + name='Module Type Profile 3', + schema=cls.PROFILE_SCHEMA + ), + ) + ModuleTypeProfile.objects.bulk_create(module_type_profiles) module_types = ( ModuleType( @@ -1455,7 +1530,14 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): weight=10, weight_unit=WeightUnitChoices.UNIT_POUND, description='foobar1', - airflow=ModuleAirflowChoices.FRONT_TO_REAR + airflow=ModuleAirflowChoices.FRONT_TO_REAR, + profile=module_type_profiles[0], + attribute_data={ + 'string': 'string1', + 'integer': 1, + 'number': 1.0, + 'boolean': True, + } ), ModuleType( manufacturer=manufacturers[1], @@ -1464,7 +1546,14 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): weight=20, weight_unit=WeightUnitChoices.UNIT_POUND, description='foobar2', - airflow=ModuleAirflowChoices.REAR_TO_FRONT + airflow=ModuleAirflowChoices.REAR_TO_FRONT, + profile=module_type_profiles[1], + attribute_data={ + 'string': 'string2', + 'integer': 2, + 'number': 2.0, + 'boolean_': False, + } ), ModuleType( manufacturer=manufacturers[2], @@ -1472,7 +1561,14 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): part_number='Part Number 3', weight=30, weight_unit=WeightUnitChoices.UNIT_KILOGRAM, - description='foobar3' + description='foobar3', + profile=module_type_profiles[2], + attribute_data={ + 'string': 'string3', + 'integer': 3, + 'number': 3.0, + 'boolean': None, + } ), ) ModuleType.objects.bulk_create(module_types) @@ -1591,6 +1687,82 @@ class ModuleTypeTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'airflow': RackAirflowChoices.FRONT_TO_REAR} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_profile(self): + profiles = ModuleTypeProfile.objects.filter(name__startswith="Module Type Profile")[:2] + params = {'profile_id': [profiles[0].pk, profiles[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'profile': [profiles[0].name, profiles[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_profile_attributes(self): + params = {'attr_string': 'string1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + params = {'attr_integer': '1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + params = {'attr_number': '2.0'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + params = {'attr_boolean': 'true'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + +class ModuleTypeProfileTestCase(TestCase, ChangeLoggedFilterSetTests): + queryset = ModuleTypeProfile.objects.all() + filterset = ModuleTypeProfileFilterSet + ignore_fields = ['schema'] + + SCHEMAS = [ + { + "properties": { + "foo": { + "type": "string" + } + } + }, + { + "properties": { + "foo": { + "type": "integer" + } + } + }, + { + "properties": { + "foo": { + "type": "boolean" + } + } + }, + ] + + @classmethod + def setUpTestData(cls): + module_type_profiles = ( + ModuleTypeProfile( + name='Module Type Profile 1', + description='foobar1', + schema=cls.SCHEMAS[0] + ), + ModuleTypeProfile( + name='Module Type Profile 2', + description='foobar2 2', + schema=cls.SCHEMAS[1] + ), + ModuleTypeProfile( + name='Module Type Profile 3', + description='foobar3', + schema=cls.SCHEMAS[2] + ), + ) + ModuleTypeProfile.objects.bulk_create(module_type_profiles) + + def test_q(self): + params = {'q': 'foobar1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_name(self): + params = {'name': ['Module Type Profile 1', 'Module Type Profile 2']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class ConsolePortTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTests, ChangeLoggedFilterSetTests): queryset = ConsolePortTemplate.objects.all() @@ -2141,12 +2313,65 @@ class DeviceRoleTestCase(TestCase, ChangeLoggedFilterSetTests): @classmethod def setUpTestData(cls): - roles = ( + parent_roles = ( DeviceRole(name='Device Role 1', slug='device-role-1', color='ff0000', vm_role=True, description='foobar1'), DeviceRole(name='Device Role 2', slug='device-role-2', color='00ff00', vm_role=True, description='foobar2'), - DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff', vm_role=False), + DeviceRole(name='Device Role 3', slug='device-role-3', color='0000ff', vm_role=False) ) - DeviceRole.objects.bulk_create(roles) + for role in parent_roles: + role.save() + + roles = ( + DeviceRole( + name='Device Role 1A', + slug='device-role-1a', + color='aa0000', + vm_role=True, + parent=parent_roles[0] + ), + DeviceRole( + name='Device Role 2A', + slug='device-role-2a', + color='00aa00', + vm_role=True, + parent=parent_roles[1] + ), + DeviceRole( + name='Device Role 3A', + slug='device-role-3a', + color='0000aa', + vm_role=False, + parent=parent_roles[2] + ) + ) + for role in roles: + role.save() + + child_roles = ( + DeviceRole( + name='Device Role 1A1', + slug='device-role-1a1', + color='bb0000', + vm_role=True, + parent=roles[0] + ), + DeviceRole( + name='Device Role 2A1', + slug='device-role-2a1', + color='00bb00', + vm_role=True, + parent=roles[1] + ), + DeviceRole( + name='Device Role 3A1', + slug='device-role-3a1', + color='0000bb', + vm_role=False, + parent=roles[2] + ) + ) + for role in child_roles: + role.save() def test_q(self): params = {'q': 'foobar1'} @@ -2166,14 +2391,28 @@ class DeviceRoleTestCase(TestCase, ChangeLoggedFilterSetTests): def test_vm_role(self): params = {'vm_role': 'true'} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6) params = {'vm_role': 'false'} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) def test_description(self): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_parent(self): + roles = DeviceRole.objects.filter(parent__isnull=True)[:2] + params = {'parent_id': [roles[0].pk, roles[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'parent': [roles[0].slug, roles[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_ancestor(self): + roles = DeviceRole.objects.filter(parent__isnull=True)[:2] + params = {'ancestor_id': [roles[0].pk, roles[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'ancestor': [roles[0].slug, roles[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + class PlatformTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = Platform.objects.all() @@ -2259,7 +2498,8 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() platforms = ( Platform(name='Platform 1', slug='platform-1'), @@ -2771,7 +3011,8 @@ class ModuleTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -3046,7 +3287,8 @@ class ConsolePortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -3258,7 +3500,8 @@ class ConsoleServerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeL DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -3476,7 +3719,8 @@ class PowerPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -3720,7 +3964,8 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -3806,6 +4051,7 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, description='First', color='ff0000', + status=PowerOutletStatusChoices.STATUS_ENABLED, ), PowerOutlet( device=devices[1], @@ -3815,6 +4061,7 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF feed_leg=PowerOutletFeedLegChoices.FEED_LEG_B, description='Second', color='00ff00', + status=PowerOutletStatusChoices.STATUS_DISABLED, ), PowerOutlet( device=devices[2], @@ -3824,6 +4071,7 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF feed_leg=PowerOutletFeedLegChoices.FEED_LEG_C, description='Third', color='0000ff', + status=PowerOutletStatusChoices.STATUS_FAULTY, ), ) PowerOutlet.objects.bulk_create(power_outlets) @@ -3918,6 +4166,23 @@ class PowerOutletTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedF params = {'connected': False} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_status(self): + params = {'status': [PowerOutletStatusChoices.STATUS_ENABLED]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'status': [PowerOutletStatusChoices.STATUS_DISABLED]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'status': [PowerOutletStatusChoices.STATUS_FAULTY]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'status': [ + PowerOutletStatusChoices.STATUS_ENABLED, + PowerOutletStatusChoices.STATUS_DISABLED, + PowerOutletStatusChoices.STATUS_FAULTY, + ]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilterSetTests): queryset = Interface.objects.all() @@ -3965,7 +4230,8 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -4544,7 +4810,8 @@ class FrontPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -4816,7 +5083,8 @@ class RearPortTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFilt DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -5056,7 +5324,8 @@ class ModuleBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -5228,7 +5497,8 @@ class DeviceBayTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() locations = ( Location(name='Location 1', slug='location-1', site=sites[0]), @@ -5363,7 +5633,8 @@ class InventoryItemTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() regions = ( Region(name='Region 1', slug='region-1'), diff --git a/netbox/dcim/tests/test_forms.py b/netbox/dcim/tests/test_forms.py index 73d5dbd98..fa654f789 100644 --- a/netbox/dcim/tests/test_forms.py +++ b/netbox/dcim/tests/test_forms.py @@ -1,11 +1,8 @@ from django.test import TestCase from dcim.choices import ( - DeviceFaceChoices, - DeviceStatusChoices, - InterfaceModeChoices, - InterfaceTypeChoices, - PortTypeChoices, + DeviceFaceChoices, DeviceStatusChoices, InterfaceModeChoices, InterfaceTypeChoices, PortTypeChoices, + PowerOutletStatusChoices, ) from dcim.forms import * from dcim.models import * @@ -18,6 +15,56 @@ def get_id(model, slug): return model.objects.get(slug=slug).id +class PowerOutletFormTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.site = site = Site.objects.create(name='Site 1', slug='site-1') + cls.manufacturer = manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') + cls.role = role = DeviceRole.objects.create( + name='Device Role 1', slug='device-role-1', color='ff0000' + ) + cls.device_type = device_type = DeviceType.objects.create( + manufacturer=manufacturer, model='Device Type 1', slug='device-type-1', u_height=1 + ) + cls.rack = rack = Rack.objects.create(name='Rack 1', site=site) + cls.device = Device.objects.create( + name='Device 1', device_type=device_type, role=role, site=site, rack=rack, position=1 + ) + + def test_status_is_required(self): + form = PowerOutletForm(data={ + 'device': self.device, + 'module': None, + 'name': 'New Enabled Outlet', + }) + self.assertFalse(form.is_valid()) + self.assertIn('status', form.errors) + + def test_status_must_be_defined_choice(self): + form = PowerOutletForm(data={ + 'device': self.device, + 'module': None, + 'name': 'New Enabled Outlet', + 'status': 'this isn\'t a defined choice', + }) + self.assertFalse(form.is_valid()) + self.assertIn('status', form.errors) + self.assertTrue(form.errors['status'][-1].startswith('Select a valid choice.')) + + def test_status_recognizes_choices(self): + for index, choice in enumerate(PowerOutletStatusChoices.CHOICES): + form = PowerOutletForm(data={ + 'device': self.device, + 'module': None, + 'name': f'New Enabled Outlet {index + 1}', + 'status': choice[0], + }) + self.assertEqual({}, form.errors) + self.assertTrue(form.is_valid()) + instance = form.save() + self.assertEqual(instance.status, choice[0]) + + class DeviceTestCase(TestCase): @classmethod diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index 398945f93..281071ed9 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -383,7 +383,8 @@ class DeviceTestCase(TestCase): DeviceRole(name='Test Role 1', slug='test-role-1'), DeviceRole(name='Test Role 2', slug='test-role-2'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() # Create a CustomField with a default value & assign it to all component models cf1 = CustomField.objects.create(name='cf1', default='foo') @@ -502,7 +503,8 @@ class DeviceTestCase(TestCase): device=device, name='Power Outlet 1', power_port=powerport, - feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A + feed_leg=PowerOutletFeedLegChoices.FEED_LEG_A, + status=PowerOutletStatusChoices.STATUS_ENABLED, ) self.assertEqual(poweroutlet.cf['cf1'], 'foo') diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index c786d1494..69192f0a1 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -1,3 +1,4 @@ +import json from decimal import Decimal from zoneinfo import ZoneInfo @@ -25,8 +26,10 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase): # Create three Regions regions = ( - Region(name='Region 1', slug='region-1'), - Region(name='Region 2', slug='region-2'), + Region(name='Region 1', slug='region-1', comments=''), + Region( + name='Region 2', slug='region-2', comments="It's going to take a lot to drag me away from you" + ), Region(name='Region 3', slug='region-3'), ) for region in regions: @@ -40,13 +43,14 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'parent': regions[2].pk, 'description': 'A new region', 'tags': [t.pk for t in tags], + 'comments': 'This comment is really exciting!', } cls.csv_data = ( - "name,slug,description", - "Region 4,region-4,Fourth region", - "Region 5,region-5,Fifth region", - "Region 6,region-6,Sixth region", + "name,slug,description,comments", + "Region 4,region-4,Fourth region,", + "Region 5,region-5,Fifth region,hi guys", + "Region 6,region-6,Sixth region,bye guys", ) cls.csv_update_data = ( @@ -58,6 +62,7 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase): cls.bulk_edit_data = { 'description': 'New description', + 'comments': 'This comment is super exciting!!!', } @@ -69,7 +74,7 @@ class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): # Create three SiteGroups sitegroups = ( - SiteGroup(name='Site Group 1', slug='site-group-1'), + SiteGroup(name='Site Group 1', slug='site-group-1', comments='Still here'), SiteGroup(name='Site Group 2', slug='site-group-2'), SiteGroup(name='Site Group 3', slug='site-group-3'), ) @@ -84,24 +89,26 @@ class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'parent': sitegroups[2].pk, 'description': 'A new site group', 'tags': [t.pk for t in tags], + 'comments': 'still here', } cls.csv_data = ( - "name,slug,description", - "Site Group 4,site-group-4,Fourth site group", - "Site Group 5,site-group-5,Fifth site group", - "Site Group 6,site-group-6,Sixth site group", + "name,slug,description,comments", + "Site Group 4,site-group-4,Fourth site group,", + "Site Group 5,site-group-5,Fifth site group,still hear", + "Site Group 6,site-group-6,Sixth site group," ) cls.csv_update_data = ( - "id,name,description", - f"{sitegroups[0].pk},Site Group 7,Fourth site group7", - f"{sitegroups[1].pk},Site Group 8,Fifth site group8", - f"{sitegroups[2].pk},Site Group 0,Sixth site group9", + "id,name,description,comments", + f"{sitegroups[0].pk},Site Group 7,Fourth site group7,", + f"{sitegroups[1].pk},Site Group 8,Fifth site group8,when will it end", + f"{sitegroups[2].pk},Site Group 0,Sixth site group9,", ) cls.bulk_edit_data = { 'description': 'New description', + 'comments': 'the end', } @@ -202,6 +209,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase): site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant, + comments='', ), Location( name='Location 2', @@ -209,6 +217,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase): site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant, + comments='First comment!', ), Location( name='Location 3', @@ -216,6 +225,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase): site=site, status=LocationStatusChoices.STATUS_ACTIVE, tenant=tenant, + comments='_This_ is a **bold comment**', ), ) for location in locations: @@ -232,24 +242,26 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'tenant': tenant.pk, 'description': 'A new location', 'tags': [t.pk for t in tags], + 'comments': 'This comment is really boring', } cls.csv_data = ( - "site,tenant,name,slug,status,description", - "Site 1,Tenant 1,Location 4,location-4,planned,Fourth location", - "Site 1,Tenant 1,Location 5,location-5,planned,Fifth location", - "Site 1,Tenant 1,Location 6,location-6,planned,Sixth location", + "site,tenant,name,slug,status,description,comments", + "Site 1,Tenant 1,Location 4,location-4,planned,Fourth location,", + "Site 1,Tenant 1,Location 5,location-5,planned,Fifth location,", + "Site 1,Tenant 1,Location 6,location-6,planned,Sixth location,hi!", ) cls.csv_update_data = ( - "id,name,description", - f"{locations[0].pk},Location 7,Fourth location7", - f"{locations[1].pk},Location 8,Fifth location8", - f"{locations[2].pk},Location 0,Sixth location9", + "id,name,description,comments", + f"{locations[0].pk},Location 7,Fourth location7,Useful comment", + f"{locations[1].pk},Location 8,Fifth location8,unuseful comment", + f"{locations[2].pk},Location 0,Sixth location9,", ) cls.bulk_edit_data = { 'description': 'New description', + 'comments': 'This comment is also really boring', } @@ -1307,6 +1319,79 @@ module-bays: self.assertEqual(response.get('Content-Type'), 'text/csv; charset=utf-8') +class ModuleTypeProfileTestCase(ViewTestCases.OrganizationalObjectViewTestCase): + model = ModuleTypeProfile + + SCHEMAS = [ + { + "properties": { + "foo": { + "type": "string" + } + } + }, + { + "properties": { + "foo": { + "type": "integer" + } + } + }, + { + "properties": { + "foo": { + "type": "boolean" + } + } + }, + ] + + @classmethod + def setUpTestData(cls): + module_type_profiles = ( + ModuleTypeProfile( + name='Module Type Profile 1', + schema=cls.SCHEMAS[0] + ), + ModuleTypeProfile( + name='Module Type Profile 2', + schema=cls.SCHEMAS[1] + ), + ModuleTypeProfile( + name='Module Type Profile 3', + schema=cls.SCHEMAS[2] + ), + ) + ModuleTypeProfile.objects.bulk_create(module_type_profiles) + + tags = create_tags('Alpha', 'Bravo', 'Charlie') + + cls.form_data = { + 'name': 'Module Type Profile X', + 'description': 'A new profile', + 'schema': json.dumps(cls.SCHEMAS[0]), + 'tags': [t.pk for t in tags], + } + + cls.csv_data = ( + "name,schema", + f"Module Type Profile 4,{json.dumps(cls.SCHEMAS[0])}", + f"Module Type Profile 5,{json.dumps(cls.SCHEMAS[1])}", + f"Module Type Profile 6,{json.dumps(cls.SCHEMAS[2])}", + ) + + cls.csv_update_data = ( + "id,description", + f"{module_type_profiles[0].pk},New description", + f"{module_type_profiles[1].pk},New description", + f"{module_type_profiles[2].pk},New description", + ) + + cls.bulk_edit_data = { + 'description': 'New description', + } + + # # DeviceType components # @@ -1696,13 +1781,16 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): @classmethod def setUpTestData(cls): - roles = ( + roles = [ DeviceRole(name='Device Role 1', slug='device-role-1'), DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), - ) - DeviceRole.objects.bulk_create(roles) + DeviceRole(name='Device Role 4', slug='device-role-4'), + ] + for role in roles: + role.save() + roles.append(DeviceRole.objects.create(name='Device Role 5', slug='device-role-5', parent=roles[3])) tags = create_tags('Alpha', 'Bravo', 'Charlie') cls.form_data = { @@ -1726,6 +1814,7 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase): f"{roles[0].pk},Device Role 7,New description7", f"{roles[1].pk},Device Role 8,New description8", f"{roles[2].pk},Device Role 9,New description9", + f"{roles[4].pk},Device Role 10,New description10", ) cls.bulk_edit_data = { @@ -1811,7 +1900,8 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase): DeviceRole(name='Device Role 1', slug='device-role-1'), DeviceRole(name='Device Role 2', slug='device-role-2'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() platforms = ( Platform(name='Platform 1', slug='platform-1'), @@ -2526,6 +2616,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase): 'device': device.pk, 'name': 'Power Outlet X', 'type': PowerOutletTypeChoices.TYPE_IEC_C13, + 'status': PowerOutletStatusChoices.STATUS_ENABLED, 'power_port': powerports[1].pk, 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, 'description': 'A power outlet', @@ -2536,6 +2627,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase): 'device': device.pk, 'name': 'Power Outlet [4-6]', 'type': PowerOutletTypeChoices.TYPE_IEC_C13, + 'status': PowerOutletStatusChoices.STATUS_ENABLED, 'power_port': powerports[1].pk, 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, 'description': 'A power outlet', @@ -2544,6 +2636,7 @@ class PowerOutletTestCase(ViewTestCases.DeviceComponentViewTestCase): cls.bulk_edit_data = { 'type': PowerOutletTypeChoices.TYPE_IEC_C15, + 'status': PowerOutletStatusChoices.STATUS_ENABLED, 'power_port': powerports[1].pk, 'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B, 'description': 'New description', diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index bcfd32707..122593834 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -37,6 +37,9 @@ urlpatterns = [ path('device-types/', include(get_model_urls('dcim', 'devicetype', detail=False))), path('device-types//', include(get_model_urls('dcim', 'devicetype'))), + path('module-type-profiles/', include(get_model_urls('dcim', 'moduletypeprofile', detail=False))), + path('module-type-profiles//', include(get_model_urls('dcim', 'moduletypeprofile'))), + path('module-types/', include(get_model_urls('dcim', 'moduletype', detail=False))), path('module-types//', include(get_model_urls('dcim', 'moduletype'))), diff --git a/netbox/dcim/utils.py b/netbox/dcim/utils.py index 4d4228490..0931761bf 100644 --- a/netbox/dcim/utils.py +++ b/netbox/dcim/utils.py @@ -1,3 +1,4 @@ +from django.apps import apps from django.contrib.contenttypes.models import ContentType from django.db import transaction @@ -56,3 +57,22 @@ def rebuild_paths(terminations): for cp in cable_paths: cp.delete() create_cablepath(cp.origins) + + +def update_interface_bridges(device, interface_templates, module=None): + """ + Used for device and module instantiation. Iterates all InterfaceTemplates with a bridge assigned + and applies it to the actual interfaces. + """ + Interface = apps.get_model('dcim', 'Interface') + + for interface_template in interface_templates.exclude(bridge=None): + interface = Interface.objects.get(device=device, name=interface_template.resolve_name(module=module)) + + if interface_template.bridge: + interface.bridge = Interface.objects.get( + device=device, + name=interface_template.bridge.resolve_name(module=module) + ) + interface.full_clean() + interface.save() diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index e878c9a86..faa9f6bb6 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -17,7 +17,6 @@ from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.views import generic -from tenancy.views import ObjectContactsView from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.permissions import get_permission_for_model @@ -314,11 +313,6 @@ class RegionBulkDeleteView(generic.BulkDeleteView): table = tables.RegionTable -@register_model_view(Region, 'contacts') -class RegionContactsView(ObjectContactsView): - queryset = Region.objects.all() - - # # Site groups # @@ -445,11 +439,6 @@ class SiteGroupBulkDeleteView(generic.BulkDeleteView): table = tables.SiteGroupTable -@register_model_view(SiteGroup, 'contacts') -class SiteGroupContactsView(ObjectContactsView): - queryset = SiteGroup.objects.all() - - # # Sites # @@ -529,11 +518,6 @@ class SiteBulkDeleteView(generic.BulkDeleteView): table = tables.SiteTable -@register_model_view(Site, 'contacts') -class SiteContactsView(ObjectContactsView): - queryset = Site.objects.all() - - # # Locations # @@ -644,11 +628,6 @@ class LocationBulkDeleteView(generic.BulkDeleteView): table = tables.LocationTable -@register_model_view(Location, 'contacts') -class LocationContactsView(ObjectContactsView): - queryset = Location.objects.all() - - # # Rack roles # @@ -946,11 +925,6 @@ class RackBulkDeleteView(generic.BulkDeleteView): table = tables.RackTable -@register_model_view(Rack, 'contacts') -class RackContactsView(ObjectContactsView): - queryset = Rack.objects.all() - - # # Rack reservations # @@ -1089,11 +1063,6 @@ class ManufacturerBulkDeleteView(generic.BulkDeleteView): table = tables.ManufacturerTable -@register_model_view(Manufacturer, 'contacts') -class ManufacturerContactsView(ObjectContactsView): - queryset = Manufacturer.objects.all() - - # # Device types # @@ -1338,6 +1307,62 @@ class DeviceTypeBulkDeleteView(generic.BulkDeleteView): table = tables.DeviceTypeTable +# +# Module type profiles +# + +@register_model_view(ModuleTypeProfile, 'list', path='', detail=False) +class ModuleTypeProfileListView(generic.ObjectListView): + queryset = ModuleTypeProfile.objects.annotate( + instance_count=count_related(ModuleType, 'profile') + ) + filterset = filtersets.ModuleTypeProfileFilterSet + filterset_form = forms.ModuleTypeProfileFilterForm + table = tables.ModuleTypeProfileTable + + +@register_model_view(ModuleTypeProfile) +class ModuleTypeProfileView(GetRelatedModelsMixin, generic.ObjectView): + queryset = ModuleTypeProfile.objects.all() + + +@register_model_view(ModuleTypeProfile, 'add', detail=False) +@register_model_view(ModuleTypeProfile, 'edit') +class ModuleTypeProfileEditView(generic.ObjectEditView): + queryset = ModuleTypeProfile.objects.all() + form = forms.ModuleTypeProfileForm + + +@register_model_view(ModuleTypeProfile, 'delete') +class ModuleTypeProfileDeleteView(generic.ObjectDeleteView): + queryset = ModuleTypeProfile.objects.all() + + +@register_model_view(ModuleTypeProfile, 'bulk_import', detail=False) +class ModuleTypeProfileBulkImportView(generic.BulkImportView): + queryset = ModuleTypeProfile.objects.all() + model_form = forms.ModuleTypeProfileImportForm + + +@register_model_view(ModuleTypeProfile, 'bulk_edit', path='edit', detail=False) +class ModuleTypeProfileBulkEditView(generic.BulkEditView): + queryset = ModuleTypeProfile.objects.annotate( + instance_count=count_related(Module, 'module_type') + ) + filterset = filtersets.ModuleTypeProfileFilterSet + table = tables.ModuleTypeProfileTable + form = forms.ModuleTypeProfileBulkEditForm + + +@register_model_view(ModuleTypeProfile, 'bulk_delete', path='delete', detail=False) +class ModuleTypeProfileBulkDeleteView(generic.BulkDeleteView): + queryset = ModuleTypeProfile.objects.annotate( + instance_count=count_related(Module, 'module_type') + ) + filterset = filtersets.ModuleTypeProfileFilterSet + table = tables.ModuleTypeProfileTable + + # # Module types # @@ -2382,11 +2407,6 @@ class DeviceBulkRenameView(generic.BulkRenameView): table = tables.DeviceTable -@register_model_view(Device, 'contacts') -class DeviceContactsView(ObjectContactsView): - queryset = Device.objects.all() - - # # Modules # @@ -3946,11 +3966,6 @@ class PowerPanelBulkDeleteView(generic.BulkDeleteView): table = tables.PowerPanelTable -@register_model_view(PowerPanel, 'contacts') -class PowerPanelContactsView(ObjectContactsView): - queryset = PowerPanel.objects.all() - - # # Power feeds # diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 5e799b504..07540c50d 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -12,4 +12,5 @@ from .serializers_.configcontexts import * from .serializers_.configtemplates import * from .serializers_.savedfilters import * from .serializers_.scripts import * +from .serializers_.tableconfigs import * from .serializers_.tags import * diff --git a/netbox/extras/api/serializers_/configtemplates.py b/netbox/extras/api/serializers_/configtemplates.py index c4a683c74..69652907e 100644 --- a/netbox/extras/api/serializers_/configtemplates.py +++ b/netbox/extras/api/serializers_/configtemplates.py @@ -22,6 +22,7 @@ class ConfigTemplateSerializer(TaggableModelSerializer, ValidatedModelSerializer model = ConfigTemplate fields = [ 'id', 'url', 'display_url', 'display', 'name', 'description', 'environment_params', 'template_code', - 'data_source', 'data_path', 'data_file', 'data_synced', 'tags', 'created', 'last_updated', + 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', + 'data_synced', 'tags', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/extras/api/serializers_/exporttemplates.py b/netbox/extras/api/serializers_/exporttemplates.py index 11f502a02..0d19d642c 100644 --- a/netbox/extras/api/serializers_/exporttemplates.py +++ b/netbox/extras/api/serializers_/exporttemplates.py @@ -26,8 +26,8 @@ class ExportTemplateSerializer(ValidatedModelSerializer): class Meta: model = ExportTemplate fields = [ - 'id', 'url', 'display_url', 'display', 'object_types', 'name', 'description', 'template_code', 'mime_type', - 'file_extension', 'as_attachment', 'data_source', 'data_path', 'data_file', 'data_synced', 'created', - 'last_updated', + 'id', 'url', 'display_url', 'display', 'object_types', 'name', 'description', 'environment_params', + 'template_code', 'mime_type', 'file_name', 'file_extension', 'as_attachment', 'data_source', + 'data_path', 'data_file', 'data_synced', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/extras/api/serializers_/tableconfigs.py b/netbox/extras/api/serializers_/tableconfigs.py new file mode 100644 index 000000000..7a4fb7b2a --- /dev/null +++ b/netbox/extras/api/serializers_/tableconfigs.py @@ -0,0 +1,22 @@ +from core.models import ObjectType +from extras.models import TableConfig +from netbox.api.fields import ContentTypeField +from netbox.api.serializers import ValidatedModelSerializer + +__all__ = ( + 'TableConfigSerializer', +) + + +class TableConfigSerializer(ValidatedModelSerializer): + object_type = ContentTypeField( + queryset=ObjectType.objects.all() + ) + + class Meta: + model = TableConfig + fields = [ + 'id', 'url', 'display_url', 'display', 'object_type', 'table', 'name', 'description', 'user', 'weight', + 'enabled', 'shared', 'columns', 'ordering', 'created', 'last_updated', + ] + brief_fields = ('id', 'url', 'display', 'name', 'description', 'object_type', 'table') diff --git a/netbox/extras/api/serializers_/tags.py b/netbox/extras/api/serializers_/tags.py index e4e62845a..5dc39584f 100644 --- a/netbox/extras/api/serializers_/tags.py +++ b/netbox/extras/api/serializers_/tags.py @@ -1,10 +1,16 @@ +from drf_spectacular.utils import extend_schema_field +from rest_framework import serializers + from core.models import ObjectType -from extras.models import Tag +from extras.models import Tag, TaggedItem +from netbox.api.exceptions import SerializerNotFound from netbox.api.fields import ContentTypeField, RelatedObjectCountField -from netbox.api.serializers import ValidatedModelSerializer +from netbox.api.serializers import BaseModelSerializer, ValidatedModelSerializer +from utilities.api import get_serializer_for_model __all__ = ( 'TagSerializer', + 'TaggedItemSerializer', ) @@ -21,7 +27,41 @@ class TagSerializer(ValidatedModelSerializer): class Meta: model = Tag fields = [ - 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'object_types', - 'tagged_items', 'created', 'last_updated', + 'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'weight', + 'object_types', 'tagged_items', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'color', 'description') + + +class TaggedItemSerializer(BaseModelSerializer): + object_type = ContentTypeField( + source='content_type', + read_only=True + ) + object = serializers.SerializerMethodField( + read_only=True + ) + tag = TagSerializer( + nested=True, + read_only=True + ) + + class Meta: + model = TaggedItem + fields = [ + 'id', 'url', 'display', 'object_type', 'object_id', 'object', 'tag', + ] + brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'object', 'tag') + + @extend_schema_field(serializers.JSONField()) + def get_object(self, obj): + """ + Serialize a nested representation of the tagged object. + """ + try: + serializer = get_serializer_for_model(obj.content_object) + except SerializerNotFound: + return obj.object_repr + data = serializer(obj.content_object, nested=True, context={'request': self.context['request']}).data + + return data diff --git a/netbox/extras/api/urls.py b/netbox/extras/api/urls.py index bbcb8f0ef..101808753 100644 --- a/netbox/extras/api/urls.py +++ b/netbox/extras/api/urls.py @@ -14,11 +14,13 @@ router.register('custom-field-choice-sets', views.CustomFieldChoiceSetViewSet) router.register('custom-links', views.CustomLinkViewSet) router.register('export-templates', views.ExportTemplateViewSet) router.register('saved-filters', views.SavedFilterViewSet) +router.register('table-configs', views.TableConfigViewSet) router.register('bookmarks', views.BookmarkViewSet) router.register('notifications', views.NotificationViewSet) router.register('notification-groups', views.NotificationGroupViewSet) router.register('subscriptions', views.SubscriptionViewSet) router.register('tags', views.TagViewSet) +router.register('tagged-objects', views.TaggedItemViewSet) router.register('image-attachments', views.ImageAttachmentViewSet) router.register('journal-entries', views.JournalEntryViewSet) router.register('config-contexts', views.ConfigContextViewSet) diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index e4c3c7f3e..6e9225f73 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -6,6 +6,7 @@ from rest_framework import status from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.generics import RetrieveUpdateDestroyAPIView +from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.renderers import JSONRenderer from rest_framework.response import Response from rest_framework.routers import APIRootView @@ -20,7 +21,7 @@ from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired from netbox.api.features import SyncedDataMixin from netbox.api.metadata import ContentTypeMetadata from netbox.api.renderers import TextRenderer -from netbox.api.viewsets import NetBoxModelViewSet +from netbox.api.viewsets import BaseViewSet, NetBoxModelViewSet from utilities.exceptions import RQWorkerNotRunningException from utilities.request import copy_safe_request from . import serializers @@ -130,6 +131,17 @@ class SavedFilterViewSet(NetBoxModelViewSet): filterset_class = filtersets.SavedFilterFilterSet +# +# Table Configs +# + +class TableConfigViewSet(NetBoxModelViewSet): + metadata_class = ContentTypeMetadata + queryset = TableConfig.objects.all() + serializer_class = serializers.TableConfigSerializer + filterset_class = filtersets.TableConfigFilterSet + + # # Bookmarks # @@ -172,6 +184,12 @@ class TagViewSet(NetBoxModelViewSet): filterset_class = filtersets.TagFilterSet +class TaggedItemViewSet(RetrieveModelMixin, ListModelMixin, BaseViewSet): + queryset = TaggedItem.objects.prefetch_related('content_type', 'content_object', 'tag') + serializer_class = serializers.TaggedItemSerializer + filterset_class = filtersets.TaggedItemFilterSet + + # # Image attachments # diff --git a/netbox/extras/choices.py b/netbox/extras/choices.py index 8258f4aaf..d2a5d94e5 100644 --- a/netbox/extras/choices.py +++ b/netbox/extras/choices.py @@ -199,23 +199,6 @@ class WebhookHttpMethodChoices(ChoiceSet): ) -# -# Staging -# - -class ChangeActionChoices(ChoiceSet): - - ACTION_CREATE = 'create' - ACTION_UPDATE = 'update' - ACTION_DELETE = 'delete' - - CHOICES = ( - (ACTION_CREATE, _('Create'), 'green'), - (ACTION_UPDATE, _('Update'), 'blue'), - (ACTION_DELETE, _('Delete'), 'red'), - ) - - # # Dashboard widgets # diff --git a/netbox/extras/constants.py b/netbox/extras/constants.py index fadf59c25..94f0b25ad 100644 --- a/netbox/extras/constants.py +++ b/netbox/extras/constants.py @@ -4,6 +4,9 @@ from extras.choices import LogLevelChoices # Custom fields CUSTOMFIELD_EMPTY_VALUES = (None, '', []) +# Template Export +DEFAULT_MIME_TYPE = 'text/plain; charset=utf-8' + # Webhooks HTTP_CONTENT_TYPE_JSON = 'application/json' diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py index c9cf9e037..2173dc786 100644 --- a/netbox/extras/dashboard/widgets.py +++ b/netbox/extras/dashboard/widgets.py @@ -18,6 +18,7 @@ from core.models import ObjectType from extras.choices import BookmarkOrderingChoices from utilities.object_types import object_type_identifier, object_type_name from utilities.permissions import get_permission_for_model +from utilities.proxy import resolve_proxies from utilities.querydict import dict_to_querydict from utilities.templatetags.builtins.filters import render_markdown from utilities.views import get_viewname @@ -364,7 +365,7 @@ class RSSFeedWidget(DashboardWidget): response = requests.get( url=self.config['feed_url'], headers={'User-Agent': f'NetBox/{settings.RELEASE.version}'}, - proxies=settings.HTTP_PROXIES, + proxies=resolve_proxies(url=self.config['feed_url'], context={'client': self}), timeout=3 ) response.raise_for_status() diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py index de739aa59..d05800c22 100644 --- a/netbox/extras/filters.py +++ b/netbox/extras/filters.py @@ -4,6 +4,7 @@ from .models import Tag __all__ = ( 'TagFilter', + 'TagIDFilter', ) @@ -20,3 +21,18 @@ class TagFilter(django_filters.ModelMultipleChoiceFilter): kwargs.setdefault('queryset', Tag.objects.all()) super().__init__(*args, **kwargs) + + +class TagIDFilter(django_filters.ModelMultipleChoiceFilter): + """ + Match on one or more assigned tags. If multiple tags are specified (e.g. ?tag=1&tag=2), the queryset is filtered + to objects matching all tags. + """ + def __init__(self, *args, **kwargs): + + kwargs.setdefault('field_name', 'tags__id') + kwargs.setdefault('to_field_name', 'id') + kwargs.setdefault('conjoined', True) + kwargs.setdefault('queryset', Tag.objects.all()) + + super().__init__(*args, **kwargs) diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 4f40ce500..6adad110d 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -8,10 +8,12 @@ from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet from tenancy.models import Tenant, TenantGroup from users.models import Group, User -from utilities.filters import ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter +from utilities.filters import ( + ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter +) from virtualization.models import Cluster, ClusterGroup, ClusterType from .choices import * -from .filters import TagFilter +from .filters import TagFilter, TagIDFilter from .models import * __all__ = ( @@ -30,7 +32,9 @@ __all__ = ( 'ObjectTypeFilterSet', 'SavedFilterFilterSet', 'ScriptFilterSet', + 'TableConfigFilterSet', 'TagFilterSet', + 'TaggedItemFilterSet', 'WebhookFilterSet', ) @@ -257,8 +261,8 @@ class ExportTemplateFilterSet(ChangeLoggedModelFilterSet): class Meta: model = ExportTemplate fields = ( - 'id', 'name', 'description', 'mime_type', 'file_extension', 'as_attachment', 'auto_sync_enabled', - 'data_synced', + 'id', 'name', 'description', 'mime_type', 'file_name', 'file_extension', 'as_attachment', + 'auto_sync_enabled', 'data_synced', ) def search(self, queryset, name, value): @@ -266,7 +270,8 @@ class ExportTemplateFilterSet(ChangeLoggedModelFilterSet): return queryset return queryset.filter( Q(name__icontains=value) | - Q(description__icontains=value) + Q(description__icontains=value) | + Q(file_name__icontains=value) ) @@ -322,6 +327,59 @@ class SavedFilterFilterSet(ChangeLoggedModelFilterSet): return queryset.filter(Q(enabled=False) | Q(Q(shared=False) & ~Q(user=user))) +class TableConfigFilterSet(ChangeLoggedModelFilterSet): + q = django_filters.CharFilter( + method='search', + label=_('Search'), + ) + object_type_id = django_filters.ModelMultipleChoiceFilter( + queryset=ObjectType.objects.all(), + field_name='object_type' + ) + object_type = ContentTypeFilter( + field_name='object_type' + ) + user_id = django_filters.ModelMultipleChoiceFilter( + queryset=User.objects.all(), + label=_('User (ID)'), + ) + user = django_filters.ModelMultipleChoiceFilter( + field_name='user__username', + queryset=User.objects.all(), + to_field_name='username', + label=_('User (name)'), + ) + usable = django_filters.BooleanFilter( + method='_usable' + ) + + class Meta: + model = TableConfig + fields = ('id', 'name', 'description', 'table', 'enabled', 'shared', 'weight') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(name__icontains=value) | + Q(description__icontains=value) | + Q(table__icontains=value) + ) + + def _usable(self, queryset, name, value): + """ + Return only TableConfigs that are both enabled and are shared (or belong to the current user). + """ + user = self.request.user if self.request else None + if not user or user.is_anonymous: + if value: + return queryset.filter(enabled=True, shared=True) + return queryset.filter(Q(enabled=False) | Q(shared=False)) + if value: + return queryset.filter(enabled=True).filter(Q(shared=True) | Q(user=user)) + return queryset.filter(Q(enabled=False) | Q(Q(shared=False) & ~Q(user=user))) + + class BookmarkFilterSet(BaseFilterSet): created = django_filters.DateTimeFilter() object_type_id = MultiValueNumberFilter() @@ -449,7 +507,7 @@ class TagFilterSet(ChangeLoggedModelFilterSet): class Meta: model = Tag - fields = ('id', 'name', 'slug', 'color', 'description', 'object_types') + fields = ('id', 'name', 'slug', 'color', 'weight', 'description', 'object_types') def search(self, queryset, name, value): if not value.strip(): @@ -492,6 +550,41 @@ class TagFilterSet(ChangeLoggedModelFilterSet): ) +class TaggedItemFilterSet(BaseFilterSet): + q = django_filters.CharFilter( + method='search', + label=_('Search'), + ) + object_type = ContentTypeFilter( + field_name='content_type' + ) + object_type_id = django_filters.ModelMultipleChoiceFilter( + queryset=ContentType.objects.all(), + field_name='content_type_id' + ) + tag_id = django_filters.ModelMultipleChoiceFilter( + queryset=Tag.objects.all() + ) + tag = django_filters.ModelMultipleChoiceFilter( + field_name='tag__slug', + queryset=Tag.objects.all(), + to_field_name='slug', + ) + + class Meta: + model = TaggedItem + fields = ('id', 'object_id') + + def search(self, queryset, name, value): + if not value.strip(): + return queryset + return queryset.filter( + Q(tag__name__icontains=value) | + Q(tag__slug__icontains=value) | + Q(tag__description__icontains=value) + ) + + class ConfigContextFilterSet(ChangeLoggedModelFilterSet): q = django_filters.CharFilter( method='search', @@ -665,10 +758,14 @@ class ConfigTemplateFilterSet(ChangeLoggedModelFilterSet): label=_('Data file (ID)'), ) tag = TagFilter() + tag_id = TagIDFilter() class Meta: model = ConfigTemplate - fields = ('id', 'name', 'description', 'auto_sync_enabled', 'data_synced') + fields = ( + 'id', 'name', 'description', 'mime_type', 'file_name', 'file_extension', 'as_attachment', + 'auto_sync_enabled', 'data_synced' + ) def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/extras/forms/bulk_edit.py b/netbox/extras/forms/bulk_edit.py index 30d06683b..c854a6c81 100644 --- a/netbox/extras/forms/bulk_edit.py +++ b/netbox/extras/forms/bulk_edit.py @@ -21,6 +21,7 @@ __all__ = ( 'JournalEntryBulkEditForm', 'NotificationGroupBulkEditForm', 'SavedFilterBulkEditForm', + 'TableConfigBulkEditForm', 'TagBulkEditForm', 'WebhookBulkEditForm', ) @@ -155,6 +156,10 @@ class ExportTemplateBulkEditForm(BulkEditForm): max_length=50, required=False ) + file_name = forms.CharField( + label=_('File name'), + required=False + ) file_extension = forms.CharField( label=_('File extension'), max_length=15, @@ -166,7 +171,7 @@ class ExportTemplateBulkEditForm(BulkEditForm): widget=BulkEditNullBooleanSelect() ) - nullable_fields = ('description', 'mime_type', 'file_extension') + nullable_fields = ('description', 'mime_type', 'file_name', 'file_extension') class SavedFilterBulkEditForm(BulkEditForm): @@ -197,6 +202,34 @@ class SavedFilterBulkEditForm(BulkEditForm): nullable_fields = ('description',) +class TableConfigBulkEditForm(BulkEditForm): + pk = forms.ModelMultipleChoiceField( + queryset=TableConfig.objects.all(), + widget=forms.MultipleHiddenInput + ) + description = forms.CharField( + label=_('Description'), + max_length=200, + required=False + ) + weight = forms.IntegerField( + label=_('Weight'), + required=False + ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=BulkEditNullBooleanSelect() + ) + shared = forms.NullBooleanField( + label=_('Shared'), + required=False, + widget=BulkEditNullBooleanSelect() + ) + + nullable_fields = ('description',) + + class WebhookBulkEditForm(NetBoxModelBulkEditForm): model = Webhook @@ -275,6 +308,10 @@ class TagBulkEditForm(BulkEditForm): max_length=200, required=False ) + weight = forms.IntegerField( + label=_('Weight'), + required=False + ) nullable_fields = ('description',) @@ -313,8 +350,27 @@ class ConfigTemplateBulkEditForm(BulkEditForm): max_length=200, required=False ) + mime_type = forms.CharField( + label=_('MIME type'), + max_length=50, + required=False + ) + file_name = forms.CharField( + label=_('File name'), + required=False + ) + file_extension = forms.CharField( + label=_('File extension'), + max_length=15, + required=False + ) + as_attachment = forms.NullBooleanField( + label=_('As attachment'), + required=False, + widget=BulkEditNullBooleanSelect() + ) - nullable_fields = ('description',) + nullable_fields = ('description', 'mime_type', 'file_name', 'file_extension') class JournalEntryBulkEditForm(BulkEditForm): diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index 35c1cbc22..5c62932e5 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -144,7 +144,8 @@ class ExportTemplateImportForm(CSVModelForm): class Meta: model = ExportTemplate fields = ( - 'name', 'object_types', 'description', 'mime_type', 'file_extension', 'as_attachment', 'template_code', + 'name', 'object_types', 'description', 'environment_params', 'mime_type', 'file_name', 'file_extension', + 'as_attachment', 'template_code', ) @@ -153,7 +154,8 @@ class ConfigTemplateImportForm(CSVModelForm): class Meta: model = ConfigTemplate fields = ( - 'name', 'description', 'environment_params', 'template_code', 'tags', + 'name', 'description', 'template_code', 'environment_params', 'mime_type', 'file_name', 'file_extension', + 'as_attachment', 'tags', ) @@ -232,10 +234,14 @@ class EventRuleImportForm(NetBoxModelImportForm): class TagImportForm(CSVModelForm): slug = SlugField() + weight = forms.IntegerField( + label=_('Weight'), + required=False + ) class Meta: model = Tag - fields = ('name', 'slug', 'color', 'description') + fields = ('name', 'slug', 'color', 'weight', 'description') class JournalEntryImportForm(NetBoxModelImportForm): diff --git a/netbox/extras/forms/filtersets.py b/netbox/extras/forms/filtersets.py index 9dc024128..27881f17a 100644 --- a/netbox/extras/forms/filtersets.py +++ b/netbox/extras/forms/filtersets.py @@ -31,6 +31,7 @@ __all__ = ( 'LocalConfigContextFilterForm', 'NotificationGroupFilterForm', 'SavedFilterFilterForm', + 'TableConfigFilterForm', 'TagFilterForm', 'WebhookFilterForm', ) @@ -164,9 +165,9 @@ class CustomLinkFilterForm(SavedFiltersMixin, FilterForm): class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm): model = ExportTemplate fieldsets = ( - FieldSet('q', 'filter_id'), + FieldSet('q', 'filter_id', 'object_type_id'), FieldSet('data_source_id', 'data_file_id', name=_('Data')), - FieldSet('object_type_id', 'mime_type', 'file_extension', 'as_attachment', name=_('Attributes')), + FieldSet('mime_type', 'file_name', 'file_extension', 'as_attachment', name=_('Rendering')), ) data_source_id = DynamicModelMultipleChoiceField( queryset=DataSource.objects.all(), @@ -190,6 +191,10 @@ class ExportTemplateFilterForm(SavedFiltersMixin, FilterForm): required=False, label=_('MIME type') ) + file_name = forms.CharField( + label=_('File name'), + required=False + ) file_extension = forms.CharField( label=_('File extension'), required=False @@ -251,6 +256,36 @@ class SavedFilterFilterForm(SavedFiltersMixin, FilterForm): ) +class TableConfigFilterForm(SavedFiltersMixin, FilterForm): + fieldsets = ( + FieldSet('q', 'filter_id'), + FieldSet('object_type_id', 'enabled', 'shared', 'weight', name=_('Attributes')), + ) + object_type_id = ContentTypeMultipleChoiceField( + label=_('Object types'), + queryset=ObjectType.objects.public(), + required=False + ) + enabled = forms.NullBooleanField( + label=_('Enabled'), + required=False, + widget=forms.Select( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + shared = forms.NullBooleanField( + label=_('Shared'), + required=False, + widget=forms.Select( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) + weight = forms.IntegerField( + label=_('Weight'), + required=False + ) + + class WebhookFilterForm(NetBoxModelFilterSetForm): model = Webhook fieldsets = ( @@ -325,7 +360,7 @@ class ConfigContextFilterForm(SavedFiltersMixin, FilterForm): FieldSet('q', 'filter_id', 'tag_id'), FieldSet('data_source_id', 'data_file_id', name=_('Data')), FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Location')), - FieldSet('device_type_id', 'platform_id', 'role_id', name=_('Device')), + FieldSet('device_type_id', 'platform_id', 'device_role_id', name=_('Device')), FieldSet('cluster_type_id', 'cluster_group_id', 'cluster_id', name=_('Cluster')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')) ) @@ -367,7 +402,7 @@ class ConfigContextFilterForm(SavedFiltersMixin, FilterForm): required=False, label=_('Device types') ) - role_id = DynamicModelMultipleChoiceField( + device_role_id = DynamicModelMultipleChoiceField( queryset=DeviceRole.objects.all(), required=False, label=_('Roles') @@ -414,6 +449,7 @@ class ConfigTemplateFilterForm(SavedFiltersMixin, FilterForm): fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('data_source_id', 'data_file_id', name=_('Data')), + FieldSet('mime_type', 'file_name', 'file_extension', 'as_attachment', name=_('Rendering')) ) data_source_id = DynamicModelMultipleChoiceField( queryset=DataSource.objects.all(), @@ -429,6 +465,25 @@ class ConfigTemplateFilterForm(SavedFiltersMixin, FilterForm): } ) tag = TagFilterField(ConfigTemplate) + mime_type = forms.CharField( + required=False, + label=_('MIME type') + ) + file_name = forms.CharField( + label=_('File name'), + required=False + ) + file_extension = forms.CharField( + label=_('File extension'), + required=False + ) + as_attachment = forms.NullBooleanField( + label=_('As attachment'), + required=False, + widget=forms.Select( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) class LocalConfigContextFilterForm(forms.Form): diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 299fff81b..5590dfa1a 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -2,6 +2,7 @@ import json import re from django import forms +from django.contrib.postgres.forms import SimpleArrayField from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ @@ -21,6 +22,7 @@ from utilities.forms.fields import ( ) from utilities.forms.rendering import FieldSet, ObjectAttribute from utilities.forms.widgets import ChoicesWidget, HTMXSelect +from utilities.tables import get_table_for_model from virtualization.models import Cluster, ClusterGroup, ClusterType __all__ = ( @@ -37,6 +39,7 @@ __all__ = ( 'NotificationGroupForm', 'SavedFilterForm', 'SubscriptionForm', + 'TableConfigForm', 'TagForm', 'WebhookForm', ) @@ -260,7 +263,9 @@ class ExportTemplateForm(SyncedDataMixin, forms.ModelForm): fieldsets = ( FieldSet('name', 'object_types', 'description', 'template_code', name=_('Export Template')), FieldSet('data_source', 'data_file', 'auto_sync_enabled', name=_('Data Source')), - FieldSet('mime_type', 'file_extension', 'as_attachment', name=_('Rendering')), + FieldSet( + 'mime_type', 'file_name', 'file_extension', 'environment_params', 'as_attachment', name=_('Rendering') + ), ) class Meta: @@ -313,6 +318,65 @@ class SavedFilterForm(forms.ModelForm): super().__init__(*args, initial=initial, **kwargs) +class TableConfigForm(forms.ModelForm): + object_type = ContentTypeChoiceField( + label=_('Object type'), + queryset=ObjectType.objects.all() + ) + ordering = SimpleArrayField( + base_field=forms.CharField(), + required=False, + label=_('Ordering'), + help_text=_( + "Enter a comma-separated list of column names. Prepend a name with a hyphen to reverse the order." + ) + ) + available_columns = SimpleArrayField( + base_field=forms.CharField(), + required=False, + widget=forms.SelectMultiple( + attrs={'size': 10, 'class': 'form-select'} + ), + label=_('Available Columns') + ) + columns = SimpleArrayField( + base_field=forms.CharField(), + widget=forms.SelectMultiple( + attrs={'size': 10, 'class': 'form-select select-all'} + ), + label=_('Selected Columns') + ) + + class Meta: + model = TableConfig + exclude = ('user',) + + def __init__(self, data=None, *args, **kwargs): + super().__init__(data, *args, **kwargs) + + object_type = ObjectType.objects.get(pk=get_field_value(self, 'object_type')) + model = object_type.model_class() + table_name = get_field_value(self, 'table') + table_class = get_table_for_model(model, table_name) + table = table_class([]) + + if columns := self._get_columns(): + table._set_columns(columns) + + # Initialize columns field based on table attributes + self.fields['available_columns'].widget.choices = table.available_columns + self.fields['columns'].widget.choices = table.selected_columns + + def _get_columns(self): + if self.is_bound and (columns := self.data.getlist('columns')): + return columns + if 'columns' in self.initial: + columns = self.get_initial_for_field(self.fields['columns'], 'columns') + return columns.split(',') if type(columns) is str else columns + if self.instance is not None: + return self.instance.columns + + class BookmarkForm(forms.ModelForm): object_type = ContentTypeChoiceField( label=_('Object type'), @@ -504,15 +568,19 @@ class TagForm(forms.ModelForm): queryset=ObjectType.objects.with_feature('tags'), required=False ) + weight = forms.IntegerField( + label=_('Weight'), + required=False + ) fieldsets = ( - FieldSet('name', 'slug', 'color', 'description', 'object_types', name=_('Tag')), + FieldSet('name', 'slug', 'color', 'weight', 'description', 'object_types', name=_('Tag')), ) class Meta: model = Tag fields = [ - 'name', 'slug', 'color', 'description', 'object_types', + 'name', 'slug', 'color', 'weight', 'description', 'object_types', ] @@ -641,9 +709,11 @@ class ConfigTemplateForm(SyncedDataMixin, forms.ModelForm): ) fieldsets = ( - FieldSet('name', 'description', 'environment_params', 'tags', name=_('Config Template')), - FieldSet('template_code', name=_('Content')), + FieldSet('name', 'description', 'tags', 'template_code', name=_('Config Template')), FieldSet('data_source', 'data_file', 'auto_sync_enabled', name=_('Data Source')), + FieldSet( + 'mime_type', 'file_name', 'file_extension', 'environment_params', 'as_attachment', name=_('Rendering') + ), ) class Meta: diff --git a/netbox/extras/forms/scripts.py b/netbox/extras/forms/scripts.py index 8ac476544..c237b9991 100644 --- a/netbox/extras/forms/scripts.py +++ b/netbox/extras/forms/scripts.py @@ -1,11 +1,18 @@ +import os + from django import forms +from django.conf import settings +from django.core.files.storage import storages from django.utils.translation import gettext_lazy as _ from core.choices import JobIntervalChoices -from utilities.forms.widgets import DateTimePicker, NumberWithOptions +from core.forms import ManagedFileForm +from extras.storage import ScriptFileSystemStorage from utilities.datetime import local_now +from utilities.forms.widgets import DateTimePicker, NumberWithOptions __all__ = ( + 'ScriptFileForm', 'ScriptForm', ) @@ -55,3 +62,26 @@ class ScriptForm(forms.Form): self.cleaned_data['_schedule_at'] = local_now() return self.cleaned_data + + +class ScriptFileForm(ManagedFileForm): + """ + ManagedFileForm with a custom save method to use django-storages. + """ + def save(self, *args, **kwargs): + # If a file was uploaded, save it to disk + if self.cleaned_data['upload_file']: + storage = storages.create_storage(storages.backends["scripts"]) + + filename = self.cleaned_data['upload_file'].name + if isinstance(storage, ScriptFileSystemStorage): + full_path = os.path.join(settings.SCRIPTS_ROOT, filename) + else: + full_path = filename + + self.instance.file_path = full_path + data = self.cleaned_data['upload_file'] + storage.save(filename, data) + + # need to skip ManagedFileForm save method + return super(ManagedFileForm, self).save(*args, **kwargs) diff --git a/netbox/extras/graphql/enums.py b/netbox/extras/graphql/enums.py new file mode 100644 index 000000000..f841fa1f5 --- /dev/null +++ b/netbox/extras/graphql/enums.py @@ -0,0 +1,26 @@ +import strawberry + +from extras.choices import * + +__all__ = ( + 'CustomFieldChoiceSetBaseEnum', + 'CustomFieldFilterLogicEnum', + 'CustomFieldTypeEnum', + 'CustomFieldUIEditableEnum', + 'CustomFieldUIVisibleEnum', + 'CustomLinkButtonClassEnum', + 'EventRuleActionEnum', + 'JournalEntryKindEnum', + 'WebhookHttpMethodEnum', +) + + +CustomFieldChoiceSetBaseEnum = strawberry.enum(CustomFieldChoiceSetBaseChoices.as_enum()) +CustomFieldFilterLogicEnum = strawberry.enum(CustomFieldFilterLogicChoices.as_enum(prefix='filter')) +CustomFieldTypeEnum = strawberry.enum(CustomFieldTypeChoices.as_enum(prefix='type')) +CustomFieldUIEditableEnum = strawberry.enum(CustomFieldUIEditableChoices.as_enum()) +CustomFieldUIVisibleEnum = strawberry.enum(CustomFieldUIVisibleChoices.as_enum()) +CustomLinkButtonClassEnum = strawberry.enum(CustomLinkButtonClassChoices.as_enum()) +EventRuleActionEnum = strawberry.enum(EventRuleActionChoices.as_enum()) +JournalEntryKindEnum = strawberry.enum(JournalEntryKindChoices.as_enum(prefix='kind')) +WebhookHttpMethodEnum = strawberry.enum(WebhookHttpMethodChoices.as_enum()) diff --git a/netbox/extras/graphql/filter_mixins.py b/netbox/extras/graphql/filter_mixins.py new file mode 100644 index 000000000..7e9a970f2 --- /dev/null +++ b/netbox/extras/graphql/filter_mixins.py @@ -0,0 +1,52 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from netbox.graphql.filter_lookups import JSONFilter + from .filters import * + +__all__ = ( + 'CustomFieldsFilterMixin', + 'JournalEntriesFilterMixin', + 'TagsFilterMixin', + 'ConfigContextFilterMixin', + 'TagBaseFilterMixin', +) + + +@dataclass +class CustomFieldsFilterMixin(BaseFilterMixin): + custom_field_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class JournalEntriesFilterMixin(BaseFilterMixin): + journal_entries: Annotated['JournalEntryFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class TagsFilterMixin(BaseFilterMixin): + tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() + + +@dataclass +class ConfigContextFilterMixin(BaseFilterMixin): + local_context_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class TagBaseFilterMixin(BaseFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index ff2e6a0f1..2798c4896 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -1,7 +1,26 @@ -import strawberry_django +from typing import Annotated, TYPE_CHECKING -from extras import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from extras import models +from extras.graphql.filter_mixins import TagBaseFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import SyncedDataFilterMixin + +if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from dcim.graphql.filters import ( + DeviceRoleFilter, DeviceTypeFilter, LocationFilter, PlatformFilter, RegionFilter, SiteFilter, SiteGroupFilter, + ) + from tenancy.graphql.filters import TenantFilter, TenantGroupFilter + from netbox.graphql.enums import ColorEnum + from netbox.graphql.filter_lookups import IntegerLookup, JSONFilter, StringArrayLookup, TreeNodeFilter + from users.graphql.filters import GroupFilter, UserFilter + from virtualization.graphql.filters import ClusterFilter, ClusterGroupFilter, ClusterTypeFilter + from .enums import * __all__ = ( 'ConfigContextFilter', @@ -15,84 +34,291 @@ __all__ = ( 'JournalEntryFilter', 'NotificationGroupFilter', 'SavedFilterFilter', + 'TableConfigFilter', 'TagFilter', 'WebhookFilter', ) @strawberry_django.filter(models.ConfigContext, lookups=True) -@autotype_decorator(filtersets.ConfigContextFilterSet) -class ConfigContextFilter(BaseFilterMixin): - pass +class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + description: FilterLookup[str] = strawberry_django.filter_field() + is_active: FilterLookup[bool] = strawberry_django.filter_field() + regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + site_groups: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + locations: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_types: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + roles: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + platforms: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_types: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_groups: Annotated['ClusterGroupFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + clusters: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_groups: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() + data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConfigTemplate, lookups=True) -@autotype_decorator(filtersets.ConfigTemplateFilterSet) -class ConfigTemplateFilter(BaseFilterMixin): - pass +class ConfigTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + template_code: FilterLookup[str] | None = strawberry_django.filter_field() + environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + mime_type: FilterLookup[str] | None = strawberry_django.filter_field() + file_name: FilterLookup[str] | None = strawberry_django.filter_field() + file_extension: FilterLookup[str] | None = strawberry_django.filter_field() + as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.CustomField, lookups=True) -@autotype_decorator(filtersets.CustomFieldFilterSet) -class CustomFieldFilter(BaseFilterMixin): - pass +class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + type: Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + object_types: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + group_name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + required: FilterLookup[bool] | None = strawberry_django.filter_field() + unique: FilterLookup[bool] | None = strawberry_django.filter_field() + search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + filter_logic: Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + default: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + related_object_filter: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + validation_minimum: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + validation_maximum: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + validation_regex: FilterLookup[str] | None = strawberry_django.filter_field() + choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + choice_set_id: ID | None = strawberry_django.filter_field() + ui_visible: Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + ui_editable: Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field() + comments: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.CustomFieldChoiceSet, lookups=True) -@autotype_decorator(filtersets.CustomFieldChoiceSetFilterSet) -class CustomFieldChoiceSetFilter(BaseFilterMixin): - pass +class CustomFieldChoiceSetFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + base_choices: Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + extra_choices: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.CustomLink, lookups=True) -@autotype_decorator(filtersets.CustomLinkFilterSet) -class CustomLinkFilter(BaseFilterMixin): - pass +class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + link_text: FilterLookup[str] | None = strawberry_django.filter_field() + link_url: FilterLookup[str] | None = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + group_name: FilterLookup[str] | None = strawberry_django.filter_field() + button_class: Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + new_window: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ExportTemplate, lookups=True) -@autotype_decorator(filtersets.ExportTemplateFilterSet) -class ExportTemplateFilter(BaseFilterMixin): - pass +class ExportTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + template_code: FilterLookup[str] | None = strawberry_django.filter_field() + environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + mime_type: FilterLookup[str] | None = strawberry_django.filter_field() + file_name: FilterLookup[str] | None = strawberry_django.filter_field() + file_extension: FilterLookup[str] | None = strawberry_django.filter_field() + as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ImageAttachment, lookups=True) -@autotype_decorator(filtersets.ImageAttachmentFilterSet) -class ImageAttachmentFilter(BaseFilterMixin): - pass +class ImageAttachmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + object_id: ID | None = strawberry_django.filter_field() + image_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.JournalEntry, lookups=True) -@autotype_decorator(filtersets.JournalEntryFilterSet) -class JournalEntryFilter(BaseFilterMixin): - pass +class JournalEntryFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_type_id: ID | None = strawberry_django.filter_field() + assigned_object_id: ID | None = strawberry_django.filter_field() + created_by: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + kind: Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + comments: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.NotificationGroup, lookups=True) -@autotype_decorator(filtersets.NotificationGroupFilterSet) -class NotificationGroupFilter(BaseFilterMixin): - pass +class NotificationGroupFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.SavedFilter, lookups=True) -@autotype_decorator(filtersets.SavedFilterFilterSet) -class SavedFilterFilter(BaseFilterMixin): - pass +class SavedFilterFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + user_id: ID | None = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + shared: FilterLookup[bool] | None = strawberry_django.filter_field() + parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + +@strawberry_django.filter(models.TableConfig, lookups=True) +class TableConfigFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + user_id: ID | None = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + shared: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Tag, lookups=True) -@autotype_decorator(filtersets.TagFilterSet) -class TagFilter(BaseFilterMixin): - pass +class TagFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin, TagBaseFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Webhook, lookups=True) -@autotype_decorator(filtersets.WebhookFilterSet) -class WebhookFilter(BaseFilterMixin): - pass +class WebhookFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + payload_url: FilterLookup[str] | None = strawberry_django.filter_field() + http_method: Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + http_content_type: FilterLookup[str] | None = strawberry_django.filter_field() + additional_headers: FilterLookup[str] | None = strawberry_django.filter_field() + body_template: FilterLookup[str] | None = strawberry_django.filter_field() + secret: FilterLookup[str] | None = strawberry_django.filter_field() + ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field() + ca_file_path: FilterLookup[str] | None = strawberry_django.filter_field() + events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.EventRule, lookups=True) -@autotype_decorator(filtersets.EventRuleFilterSet) -class EventRuleFilter(BaseFilterMixin): - pass +class EventRuleFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + conditions: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + action_type: Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + action_object_type: FilterLookup[str] | 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_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + comments: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/extras/graphql/schema.py b/netbox/extras/graphql/schema.py index 7d2d11bf1..947ff0b00 100644 --- a/netbox/extras/graphql/schema.py +++ b/netbox/extras/graphql/schema.py @@ -32,6 +32,9 @@ class ExtrasQuery: saved_filter: SavedFilterType = strawberry_django.field() saved_filter_list: List[SavedFilterType] = strawberry_django.field() + table_config: TableConfigType = strawberry_django.field() + table_config_list: List[TableConfigType] = strawberry_django.field() + journal_entry: JournalEntryType = strawberry_django.field() journal_entry_list: List[JournalEntryType] = strawberry_django.field() diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index a53c7bed3..4bd836f6b 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django @@ -8,6 +8,22 @@ from extras.graphql.mixins import CustomFieldsMixin, TagsMixin from netbox.graphql.types import BaseObjectType, ContentTypeType, ObjectType, OrganizationalObjectType from .filters import * +if TYPE_CHECKING: + from core.graphql.types import DataFileType, DataSourceType + from dcim.graphql.types import ( + DeviceRoleType, + DeviceType, + DeviceTypeType, + LocationType, + PlatformType, + RegionType, + SiteGroupType, + SiteType, + ) + from tenancy.graphql.types import TenantGroupType, TenantType + from users.graphql.types import GroupType, UserType + from virtualization.graphql.types import ClusterGroupType, ClusterType, ClusterTypeType, VirtualMachineType + __all__ = ( 'ConfigContextType', 'ConfigTemplateType', @@ -22,6 +38,7 @@ __all__ = ( 'NotificationType', 'SavedFilterType', 'SubscriptionType', + 'TableConfigType', 'TagType', 'WebhookType', ) @@ -30,12 +47,12 @@ __all__ = ( @strawberry_django.type( models.ConfigContext, fields='__all__', - filters=ConfigContextFilter + filters=ConfigContextFilter, + pagination=True ) class ConfigContextType(ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None data_file: Annotated["DataFileType", strawberry.lazy('core.graphql.types')] | None - roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]] device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]] tags: List[Annotated["TagType", strawberry.lazy('extras.graphql.types')]] @@ -54,7 +71,8 @@ class ConfigContextType(ObjectType): @strawberry_django.type( models.ConfigTemplate, fields='__all__', - filters=ConfigTemplateFilter + filters=ConfigTemplateFilter, + pagination=True ) class ConfigTemplateType(TagsMixin, ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None @@ -69,7 +87,8 @@ class ConfigTemplateType(TagsMixin, ObjectType): @strawberry_django.type( models.CustomField, fields='__all__', - filters=CustomFieldFilter + filters=CustomFieldFilter, + pagination=True ) class CustomFieldType(ObjectType): related_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None @@ -78,8 +97,9 @@ class CustomFieldType(ObjectType): @strawberry_django.type( models.CustomFieldChoiceSet, - exclude=('extra_choices', ), - filters=CustomFieldChoiceSetFilter + exclude=['extra_choices'], + filters=CustomFieldChoiceSetFilter, + pagination=True ) class CustomFieldChoiceSetType(ObjectType): @@ -90,7 +110,8 @@ class CustomFieldChoiceSetType(ObjectType): @strawberry_django.type( models.CustomLink, fields='__all__', - filters=CustomLinkFilter + filters=CustomLinkFilter, + pagination=True ) class CustomLinkType(ObjectType): pass @@ -99,7 +120,8 @@ class CustomLinkType(ObjectType): @strawberry_django.type( models.ExportTemplate, fields='__all__', - filters=ExportTemplateFilter + filters=ExportTemplateFilter, + pagination=True ) class ExportTemplateType(ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None @@ -109,7 +131,8 @@ class ExportTemplateType(ObjectType): @strawberry_django.type( models.ImageAttachment, fields='__all__', - filters=ImageAttachmentFilter + filters=ImageAttachmentFilter, + pagination=True ) class ImageAttachmentType(BaseObjectType): object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None @@ -118,7 +141,8 @@ class ImageAttachmentType(BaseObjectType): @strawberry_django.type( models.JournalEntry, fields='__all__', - filters=JournalEntryFilter + filters=JournalEntryFilter, + pagination=True ) class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): assigned_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None @@ -128,6 +152,7 @@ class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): @strawberry_django.type( models.Notification, # filters=NotificationFilter + pagination=True ) class NotificationType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None @@ -135,7 +160,8 @@ class NotificationType(ObjectType): @strawberry_django.type( models.NotificationGroup, - filters=NotificationGroupFilter + filters=NotificationGroupFilter, + pagination=True ) class NotificationGroupType(ObjectType): users: List[Annotated["UserType", strawberry.lazy('users.graphql.types')]] @@ -145,7 +171,8 @@ class NotificationGroupType(ObjectType): @strawberry_django.type( models.SavedFilter, exclude=['content_types',], - filters=SavedFilterFilter + filters=SavedFilterFilter, + pagination=True ) class SavedFilterType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None @@ -154,15 +181,27 @@ class SavedFilterType(ObjectType): @strawberry_django.type( models.Subscription, # filters=NotificationFilter + pagination=True ) class SubscriptionType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None +@strawberry_django.type( + models.TableConfig, + fields='__all__', + filters=TableConfigFilter, + pagination=True +) +class TableConfigType(ObjectType): + user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None + + @strawberry_django.type( models.Tag, exclude=['extras_taggeditem_items', ], - filters=TagFilter + filters=TagFilter, + pagination=True ) class TagType(ObjectType): color: str @@ -173,7 +212,8 @@ class TagType(ObjectType): @strawberry_django.type( models.Webhook, exclude=['content_types',], - filters=WebhookFilter + filters=WebhookFilter, + pagination=True ) class WebhookType(OrganizationalObjectType): pass @@ -182,7 +222,8 @@ class WebhookType(OrganizationalObjectType): @strawberry_django.type( models.EventRule, exclude=['content_types',], - filters=EventRuleFilter + filters=EventRuleFilter, + pagination=True ) class EventRuleType(OrganizationalObjectType): action_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None diff --git a/netbox/extras/management/commands/housekeeping.py b/netbox/extras/management/commands/housekeeping.py index aafba8c0d..b8c7eab7d 100644 --- a/netbox/extras/management/commands/housekeeping.py +++ b/netbox/extras/management/commands/housekeeping.py @@ -10,6 +10,7 @@ from packaging import version from core.models import Job, ObjectChange from netbox.config import Config +from utilities.proxy import resolve_proxies class Command(BaseCommand): @@ -106,7 +107,7 @@ class Command(BaseCommand): response = requests.get( url=settings.RELEASE_CHECK_URL, headers=headers, - proxies=settings.HTTP_PROXIES + proxies=resolve_proxies(url=settings.RELEASE_CHECK_URL) ) response.raise_for_status() diff --git a/netbox/extras/migrations/0002_squashed_0059.py b/netbox/extras/migrations/0002_squashed_0059.py index b664b286e..3aa7644fd 100644 --- a/netbox/extras/migrations/0002_squashed_0059.py +++ b/netbox/extras/migrations/0002_squashed_0059.py @@ -3,10 +3,10 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('dcim', '0002_auto_20160622_1821'), - ('extras', '0001_initial'), - ('virtualization', '0001_virtualization'), - ('tenancy', '0001_initial'), + ('dcim', '0002_squashed'), + ('extras', '0001_squashed'), + ('virtualization', '0001_squashed_0022'), + ('tenancy', '0001_squashed_0012'), ] replaces = [ diff --git a/netbox/extras/migrations/0060_squashed_0086.py b/netbox/extras/migrations/0060_squashed_0086.py index 3bde7480f..2e4437c6b 100644 --- a/netbox/extras/migrations/0060_squashed_0086.py +++ b/netbox/extras/migrations/0060_squashed_0086.py @@ -45,13 +45,13 @@ class Migration(migrations.Migration): dependencies = [ ('virtualization', '0001_squashed_0022'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0001_initial'), + ('core', '0001_squashed_0005'), ('contenttypes', '0002_remove_content_type_name'), - ('wireless', '0008_wirelesslan_status'), - ('dcim', '0166_virtualdevicecontext'), - ('tenancy', '0009_standardize_description_comments'), - ('extras', '0059_exporttemplate_as_attachment'), - ('circuits', '0041_standardize_description_comments'), + ('wireless', '0001_squashed_0008'), + ('dcim', '0160_squashed_0166'), + ('tenancy', '0001_squashed_0012'), + ('extras', '0002_squashed_0059'), + ('circuits', '0038_squashed_0042'), ] operations = [ diff --git a/netbox/extras/migrations/0087_squashed_0098.py b/netbox/extras/migrations/0087_squashed_0098.py index 839f4cbe4..21a6116b7 100644 --- a/netbox/extras/migrations/0087_squashed_0098.py +++ b/netbox/extras/migrations/0087_squashed_0098.py @@ -26,9 +26,9 @@ class Migration(migrations.Migration): dependencies = [ ('contenttypes', '0002_remove_content_type_name'), - ('extras', '0086_configtemplate'), + ('extras', '0060_squashed_0086'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0002_managedfile'), + ('core', '0001_squashed_0005'), ] operations = [ diff --git a/netbox/extras/migrations/0099_cachedvalue_ordering.py b/netbox/extras/migrations/0099_cachedvalue_ordering.py index 36b91d59b..d3ddc5533 100644 --- a/netbox/extras/migrations/0099_cachedvalue_ordering.py +++ b/netbox/extras/migrations/0099_cachedvalue_ordering.py @@ -5,7 +5,7 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('extras', '0098_webhook_custom_field_data_webhook_tags'), + ('extras', '0087_squashed_0098'), ] operations = [ diff --git a/netbox/extras/migrations/0124_remove_staging.py b/netbox/extras/migrations/0124_remove_staging.py new file mode 100644 index 000000000..2dab3c92e --- /dev/null +++ b/netbox/extras/migrations/0124_remove_staging.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.5 on 2025-02-20 19:46 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0123_journalentry_kind_default'), + ] + + operations = [ + migrations.RemoveField( + model_name='stagedchange', + name='branch', + ), + migrations.RemoveField( + model_name='stagedchange', + name='object_type', + ), + migrations.DeleteModel( + name='Branch', + ), + migrations.DeleteModel( + name='StagedChange', + ), + ] diff --git a/netbox/extras/migrations/0125_alter_tag_options_tag_weight.py b/netbox/extras/migrations/0125_alter_tag_options_tag_weight.py new file mode 100644 index 000000000..90bd055ea --- /dev/null +++ b/netbox/extras/migrations/0125_alter_tag_options_tag_weight.py @@ -0,0 +1,20 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0124_remove_staging'), + ] + + operations = [ + migrations.AlterModelOptions( + name='tag', + options={'ordering': ('weight', 'name')}, + ), + migrations.AddField( + model_name='tag', + name='weight', + field=models.PositiveSmallIntegerField(default=1000), + ), + ] diff --git a/netbox/extras/migrations/0126_exporttemplate_file_name.py b/netbox/extras/migrations/0126_exporttemplate_file_name.py new file mode 100644 index 000000000..980d76258 --- /dev/null +++ b/netbox/extras/migrations/0126_exporttemplate_file_name.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0125_alter_tag_options_tag_weight'), + ] + + operations = [ + migrations.AddField( + model_name='exporttemplate', + name='file_name', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/netbox/extras/migrations/0127_configtemplate_as_attachment_and_more.py b/netbox/extras/migrations/0127_configtemplate_as_attachment_and_more.py new file mode 100644 index 000000000..1bdcb94e2 --- /dev/null +++ b/netbox/extras/migrations/0127_configtemplate_as_attachment_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.2b1 on 2025-04-04 20:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0126_exporttemplate_file_name'), + ] + + operations = [ + migrations.AddField( + model_name='configtemplate', + name='as_attachment', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='configtemplate', + name='file_extension', + field=models.CharField(blank=True, max_length=15), + ), + migrations.AddField( + model_name='configtemplate', + name='file_name', + field=models.CharField(blank=True, max_length=200), + ), + migrations.AddField( + model_name='configtemplate', + name='mime_type', + field=models.CharField(blank=True, max_length=50), + ), + migrations.AddField( + model_name='exporttemplate', + name='environment_params', + field=models.JSONField(blank=True, default=dict, null=True), + ), + ] diff --git a/netbox/extras/migrations/0128_tableconfig.py b/netbox/extras/migrations/0128_tableconfig.py new file mode 100644 index 000000000..e6d45199d --- /dev/null +++ b/netbox/extras/migrations/0128_tableconfig.py @@ -0,0 +1,56 @@ +import django.contrib.postgres.fields +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('core', '0015_remove_redundant_indexes'), + ('extras', '0127_configtemplate_as_attachment_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='TableConfig', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('table', models.CharField(max_length=100)), + ('name', models.CharField(max_length=100)), + ('description', models.CharField(blank=True, max_length=200)), + ('weight', models.PositiveSmallIntegerField(default=1000)), + ('enabled', models.BooleanField(default=True)), + ('shared', models.BooleanField(default=True)), + ( + 'columns', + django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), size=None), + ), + ( + 'ordering', + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), blank=True, null=True, size=None + ), + ), + ( + 'object_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='table_configs', to='core.objecttype' + ), + ), + ( + 'user', + models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + 'verbose_name': 'table config', + 'verbose_name_plural': 'table configs', + 'ordering': ('weight', 'name'), + }, + ), + ] diff --git a/netbox/extras/models/__init__.py b/netbox/extras/models/__init__.py index e85721034..f214b1268 100644 --- a/netbox/extras/models/__init__.py +++ b/netbox/extras/models/__init__.py @@ -5,5 +5,4 @@ from .models import * from .notifications import * from .scripts import * from .search import * -from .staging import * from .tags import * diff --git a/netbox/extras/models/configs.py b/netbox/extras/models/configs.py index 773f5a70e..8d6b8d999 100644 --- a/netbox/extras/models/configs.py +++ b/netbox/extras/models/configs.py @@ -4,16 +4,13 @@ from django.core.validators import ValidationError from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from jinja2.loaders import BaseLoader -from jinja2.sandbox import SandboxedEnvironment +from extras.models.mixins import RenderTemplateMixin from extras.querysets import ConfigContextQuerySet -from netbox.config import get_config from netbox.models import ChangeLoggedModel from netbox.models.features import CloningMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin from netbox.registry import registry from utilities.data import deepmerge -from utilities.jinja2 import DataFileLoader __all__ = ( 'ConfigContext', @@ -210,7 +207,9 @@ class ConfigContextModel(models.Model): # Config templates # -class ConfigTemplate(SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel): +class ConfigTemplate( + RenderTemplateMixin, SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel +): name = models.CharField( verbose_name=_('name'), max_length=100 @@ -220,20 +219,6 @@ class ConfigTemplate(SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, Ta max_length=200, blank=True ) - template_code = models.TextField( - verbose_name=_('template code'), - help_text=_('Jinja2 template code.') - ) - environment_params = models.JSONField( - verbose_name=_('environment parameters'), - blank=True, - null=True, - default=dict, - help_text=_( - 'Any additional parameters' - ' to pass when constructing the Jinja2 environment.' - ) - ) class Meta: ordering = ('name',) @@ -253,13 +238,8 @@ class ConfigTemplate(SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, Ta self.template_code = self.data_file.data_as_string sync_data.alters_data = True - def render(self, context=None): - """ - Render the contents of the template. - """ + def get_context(self, context=None, queryset=None): _context = dict() - - # Populate the default template context with NetBox model classes, namespaced by app for app, model_names in registry['models'].items(): _context.setdefault(app, {}) for model_name in model_names: @@ -269,37 +249,8 @@ class ConfigTemplate(SyncedDataMixin, CustomLinksMixin, ExportTemplatesMixin, Ta except LookupError: pass - # Add the provided context data, if any + # Apply the provided context data, if any if context is not None: _context.update(context) - # Initialize the Jinja2 environment and instantiate the Template - environment = self._get_environment() - if self.data_file: - template = environment.get_template(self.data_file.path) - else: - template = environment.from_string(self.template_code) - output = template.render(**_context) - - # Replace CRLF-style line terminators - return output.replace('\r\n', '\n') - - def _get_environment(self): - """ - Instantiate and return a Jinja2 environment suitable for rendering the ConfigTemplate. - """ - # Initialize the template loader & cache the base template code (if applicable) - if self.data_file: - loader = DataFileLoader(data_source=self.data_source) - loader.cache_templates({ - self.data_file.path: self.template_code - }) - else: - loader = BaseLoader() - - # Initialize the environment - env_params = self.environment_params or {} - environment = SandboxedEnvironment(loader=loader, **env_params) - environment.filters.update(get_config().JINJA2_FILTERS) - - return environment + return _context diff --git a/netbox/extras/models/mixins.py b/netbox/extras/models/mixins.py index 0950324c8..3a7273f93 100644 --- a/netbox/extras/models/mixins.py +++ b/netbox/extras/models/mixins.py @@ -1,11 +1,40 @@ +import importlib.abc +import importlib.util import os -from importlib.machinery import SourceFileLoader +import sys +from django.core.files.storage import storages +from django.db import models +from django.utils.translation import gettext_lazy as _ +from django.http import HttpResponse + +from extras.constants import DEFAULT_MIME_TYPE +from extras.utils import filename_from_model, filename_from_object +from utilities.jinja2 import render_jinja2 + __all__ = ( 'PythonModuleMixin', + 'RenderTemplateMixin', ) +class CustomStoragesLoader(importlib.abc.Loader): + """ + Custom loader for exec_module to use django-storages instead of the file system. + """ + def __init__(self, filename): + self.filename = filename + + def create_module(self, spec): + return None # Use default module creation + + def exec_module(self, module): + storage = storages.create_storage(storages.backends["scripts"]) + with storage.open(self.filename, 'rb') as f: + code = f.read() + exec(code, module.__dict__) + + class PythonModuleMixin: def get_jobs(self, name): @@ -33,6 +62,99 @@ class PythonModuleMixin: return name def get_module(self): - loader = SourceFileLoader(self.python_name, self.full_path) - module = loader.load_module() + """ + Load the module using importlib, but use a custom loader to use django-storages + instead of the file system. + """ + spec = importlib.util.spec_from_file_location(self.python_name, self.name) + if spec is None: + raise ModuleNotFoundError(f"Could not find module: {self.python_name}") + loader = CustomStoragesLoader(self.name) + module = importlib.util.module_from_spec(spec) + sys.modules[self.python_name] = module + loader.exec_module(module) + return module + + +class RenderTemplateMixin(models.Model): + """ + Enables support for rendering templates. + """ + template_code = models.TextField( + verbose_name=_('template code'), + help_text=_('Jinja template code.') + ) + environment_params = models.JSONField( + verbose_name=_('environment parameters'), + blank=True, + null=True, + default=dict, + help_text=_( + 'Any additional parameters to pass when constructing the Jinja environment' + ).format(url='https://jinja.palletsprojects.com/en/stable/api/#jinja2.Environment') + ) + mime_type = models.CharField( + max_length=50, + blank=True, + verbose_name=_('MIME type'), + help_text=_('Defaults to {default}').format(default=DEFAULT_MIME_TYPE), + ) + file_name = models.CharField( + max_length=200, + blank=True, + help_text=_('Filename to give to the rendered export file') + ) + file_extension = models.CharField( + verbose_name=_('file extension'), + max_length=15, + blank=True, + help_text=_('Extension to append to the rendered filename') + ) + as_attachment = models.BooleanField( + verbose_name=_('as attachment'), + default=True, + help_text=_("Download file as attachment") + ) + + class Meta: + abstract = True + + def get_context(self, context=None, queryset=None): + raise NotImplementedError(_("{class_name} must implement a get_context() method.").format( + class_name=self.__class__ + )) + + def render(self, context=None, queryset=None): + """ + Render the template with the provided context. The context is passed to the Jinja2 environment as a dictionary. + """ + context = self.get_context(context=context, queryset=queryset) + env_params = self.environment_params or {} + output = render_jinja2(self.template_code, context, env_params) + + # Replace CRLF-style line terminators + output = output.replace('\r\n', '\n') + + return output + + def render_to_response(self, context=None, queryset=None): + output = self.render(context=context, queryset=queryset) + mime_type = self.mime_type or DEFAULT_MIME_TYPE + + # Build the response + response = HttpResponse(output, content_type=mime_type) + + if self.as_attachment: + extension = f'.{self.file_extension}' if self.file_extension else '' + if self.file_name: + filename = self.file_name + elif queryset: + filename = filename_from_model(queryset.model) + elif context: + filename = filename_from_object(context) + else: + filename = "output" + response['Content-Disposition'] = f'attachment; filename="{filename}{extension}"' + + return response diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index d3e443b14..9da2a8d9e 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -6,7 +6,6 @@ from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelatio from django.contrib.postgres.fields import ArrayField from django.core.validators import ValidationError from django.db import models -from django.http import HttpResponse from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -17,16 +16,18 @@ from extras.choices import * from extras.conditions import ConditionSet from extras.constants import * from extras.utils import image_upload +from extras.models.mixins import RenderTemplateMixin from netbox.config import get_config from netbox.events import get_event_type_choices from netbox.models import ChangeLoggedModel from netbox.models.features import ( - CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin, + CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, SyncedDataMixin, TagsMixin ) from utilities.html import clean_html from utilities.jinja2 import render_jinja2 from utilities.querydict import dict_to_querydict from utilities.querysets import RestrictedQuerySet +from utilities.tables import get_table_for_model __all__ = ( 'Bookmark', @@ -36,6 +37,7 @@ __all__ = ( 'ImageAttachment', 'JournalEntry', 'SavedFilter', + 'TableConfig', 'Webhook', ) @@ -382,7 +384,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): } -class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): +class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, RenderTemplateMixin): object_types = models.ManyToManyField( to='core.ObjectType', related_name='export_templates', @@ -397,32 +399,9 @@ class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, Change max_length=200, blank=True ) - template_code = models.TextField( - help_text=_( - "Jinja2 template code. The list of objects being exported is passed as a context variable named " - "queryset." - ) - ) - mime_type = models.CharField( - max_length=50, - blank=True, - verbose_name=_('MIME type'), - help_text=_('Defaults to text/plain; charset=utf-8') - ) - file_extension = models.CharField( - verbose_name=_('file extension'), - max_length=15, - blank=True, - help_text=_('Extension to append to the rendered filename') - ) - as_attachment = models.BooleanField( - verbose_name=_('as attachment'), - default=True, - help_text=_("Download file as attachment") - ) clone_fields = ( - 'object_types', 'template_code', 'mime_type', 'file_extension', 'as_attachment', + 'object_types', 'template_code', 'mime_type', 'file_name', 'file_extension', 'as_attachment', ) class Meta: @@ -455,37 +434,16 @@ class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, Change self.template_code = self.data_file.data_as_string sync_data.alters_data = True - def render(self, queryset): - """ - Render the contents of the template. - """ - context = { - 'queryset': queryset + def get_context(self, context=None, queryset=None): + _context = { + 'queryset': queryset, } - output = render_jinja2(self.template_code, context) - # Replace CRLF-style line terminators - output = output.replace('\r\n', '\n') + # Apply the provided context data, if any + if context is not None: + _context.update(context) - return output - - def render_to_response(self, queryset): - """ - Render the template to an HTTP response, delivered as a named file attachment - """ - output = self.render(queryset) - mime_type = 'text/plain; charset=utf-8' if not self.mime_type else self.mime_type - - # Build the response - response = HttpResponse(output, content_type=mime_type) - - if self.as_attachment: - basename = queryset.model._meta.verbose_name_plural.replace(' ', '_') - extension = f'.{self.file_extension}' if self.file_extension else '' - filename = f'netbox_{basename}{extension}' - response['Content-Disposition'] = f'attachment; filename="{filename}"' - - return response + return _context class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): @@ -568,6 +526,121 @@ class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): return qd.urlencode() +class TableConfig(CloningMixin, ChangeLoggedModel): + """ + A saved configuration of columns and ordering which applies to a specific table. + """ + object_type = models.ForeignKey( + to='core.ObjectType', + on_delete=models.CASCADE, + related_name='table_configs', + help_text=_("The table's object type"), + ) + table = models.CharField( + verbose_name=_('table'), + max_length=100, + ) + name = models.CharField( + verbose_name=_('name'), + max_length=100, + ) + description = models.CharField( + verbose_name=_('description'), + max_length=200, + blank=True, + ) + user = models.ForeignKey( + to=settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + weight = models.PositiveSmallIntegerField( + verbose_name=_('weight'), + default=1000, + ) + enabled = models.BooleanField( + verbose_name=_('enabled'), + default=True + ) + shared = models.BooleanField( + verbose_name=_('shared'), + default=True + ) + columns = ArrayField( + base_field=models.CharField(max_length=100), + ) + ordering = ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + null=True, + ) + + clone_fields = ('object_type', 'table', 'enabled', 'shared', 'columns', 'ordering') + + class Meta: + ordering = ('weight', 'name') + verbose_name = _('table config') + verbose_name_plural = _('table configs') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('extras:tableconfig', args=[self.pk]) + + @property + def docs_url(self): + return f'{settings.STATIC_URL}docs/models/extras/tableconfig/' + + @property + def table_class(self): + return get_table_for_model(self.object_type.model_class(), name=self.table) + + @property + def ordering_items(self): + """ + Return a list of two-tuples indicating the column(s) by which the table is to be ordered and a boolean for each + column indicating whether its ordering is ascending. + """ + items = [] + for col in self.ordering or []: + if col.startswith('-'): + ascending = False + col = col[1:] + else: + ascending = True + items.append((col, ascending)) + return items + + def clean(self): + super().clean() + + # Validate table + if self.table_class is None: + raise ValidationError({ + 'table': _("Unknown table: {name}").format(name=self.table) + }) + + table = self.table_class([]) + + # Validate ordering columns + for name in self.ordering: + if name.startswith('-'): + name = name[1:] # Strip leading hyphen + if name not in table.columns: + raise ValidationError({ + 'ordering': _('Unknown column: {name}').format(name=name) + }) + + # Validate selected columns + for name in self.columns: + if name not in table.columns: + raise ValidationError({ + 'columns': _('Unknown column: {name}').format(name=name) + }) + + class ImageAttachment(ChangeLoggedModel): """ An uploaded image which is associated with an object. diff --git a/netbox/extras/models/staging.py b/netbox/extras/models/staging.py deleted file mode 100644 index 68d37de7f..000000000 --- a/netbox/extras/models/staging.py +++ /dev/null @@ -1,150 +0,0 @@ -import logging -import warnings - -from django.contrib.contenttypes.fields import GenericForeignKey -from django.db import models, transaction -from django.utils.translation import gettext_lazy as _ -from mptt.models import MPTTModel - -from extras.choices import ChangeActionChoices -from netbox.models import ChangeLoggedModel -from netbox.models.features import * -from utilities.serialization import deserialize_object - -__all__ = ( - 'Branch', - 'StagedChange', -) - -logger = logging.getLogger('netbox.staging') - - -class Branch(ChangeLoggedModel): - """ - A collection of related StagedChanges. - """ - name = models.CharField( - verbose_name=_('name'), - max_length=100, - unique=True - ) - description = models.CharField( - verbose_name=_('description'), - max_length=200, - blank=True - ) - user = models.ForeignKey( - to='users.User', - on_delete=models.SET_NULL, - blank=True, - null=True - ) - - class Meta: - ordering = ('name',) - verbose_name = _('branch') - verbose_name_plural = _('branches') - - def __init__(self, *args, **kwargs): - warnings.warn( - 'The staged changes functionality has been deprecated and will be removed in a future release.', - DeprecationWarning - ) - super().__init__(*args, **kwargs) - - def __str__(self): - return f'{self.name} ({self.pk})' - - def merge(self): - logger.info(f'Merging changes in branch {self}') - with transaction.atomic(): - for change in self.staged_changes.all(): - change.apply() - self.staged_changes.all().delete() - - -class StagedChange(CustomValidationMixin, EventRulesMixin, models.Model): - """ - The prepared creation, modification, or deletion of an object to be applied to the active database at a - future point. - """ - branch = models.ForeignKey( - to=Branch, - on_delete=models.CASCADE, - related_name='staged_changes' - ) - action = models.CharField( - verbose_name=_('action'), - max_length=20, - choices=ChangeActionChoices - ) - object_type = models.ForeignKey( - to='contenttypes.ContentType', - on_delete=models.CASCADE, - related_name='+' - ) - object_id = models.PositiveBigIntegerField( - blank=True, - null=True - ) - object = GenericForeignKey( - ct_field='object_type', - fk_field='object_id' - ) - data = models.JSONField( - verbose_name=_('data'), - blank=True, - null=True - ) - - class Meta: - ordering = ('pk',) - indexes = ( - models.Index(fields=('object_type', 'object_id')), - ) - verbose_name = _('staged change') - verbose_name_plural = _('staged changes') - - def __init__(self, *args, **kwargs): - warnings.warn( - 'The staged changes functionality has been deprecated and will be removed in a future release.', - DeprecationWarning - ) - super().__init__(*args, **kwargs) - - def __str__(self): - action = self.get_action_display() - app_label, model_name = self.object_type.natural_key() - return f"{action} {app_label}.{model_name} ({self.object_id})" - - @property - def model(self): - return self.object_type.model_class() - - def apply(self): - """ - Apply the staged create/update/delete action to the database. - """ - if self.action == ChangeActionChoices.ACTION_CREATE: - instance = deserialize_object(self.model, self.data, pk=self.object_id) - logger.info(f'Creating {self.model._meta.verbose_name} {instance}') - instance.save() - - if self.action == ChangeActionChoices.ACTION_UPDATE: - instance = deserialize_object(self.model, self.data, pk=self.object_id) - logger.info(f'Updating {self.model._meta.verbose_name} {instance}') - instance.save() - - if self.action == ChangeActionChoices.ACTION_DELETE: - instance = self.model.objects.get(pk=self.object_id) - logger.info(f'Deleting {self.model._meta.verbose_name} {instance}') - instance.delete() - - # Rebuild the MPTT tree where applicable - if issubclass(self.model, MPTTModel): - self.model.objects.rebuild() - - apply.alters_data = True - - def get_action_color(self): - return ChangeActionChoices.colors.get(self.action) diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py index d1e329f03..b40327265 100644 --- a/netbox/extras/models/tags.py +++ b/netbox/extras/models/tags.py @@ -9,6 +9,7 @@ from netbox.choices import ColorChoices from netbox.models import ChangeLoggedModel from netbox.models.features import CloningMixin, ExportTemplatesMixin from utilities.fields import ColorField +from utilities.querysets import RestrictedQuerySet __all__ = ( 'Tag', @@ -39,13 +40,17 @@ class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase): blank=True, help_text=_("The object type(s) to which this tag can be applied.") ) + weight = models.PositiveSmallIntegerField( + verbose_name=_('weight'), + default=1000, + ) clone_fields = ( 'color', 'description', 'object_types', ) class Meta: - ordering = ['name'] + ordering = ('weight', 'name') verbose_name = _('tag') verbose_name_plural = _('tags') @@ -72,6 +77,7 @@ class TaggedItem(GenericTaggedItemBase): ) _netbox_private = True + objects = RestrictedQuerySet.as_manager() class Meta: indexes = [models.Index(fields=["content_type", "object_id"])] diff --git a/netbox/extras/querysets.py b/netbox/extras/querysets.py index 59be85734..8d6628a83 100644 --- a/netbox/extras/querysets.py +++ b/netbox/extras/querysets.py @@ -22,8 +22,6 @@ class ConfigContextQuerySet(RestrictedQuerySet): aggregate_data: If True, use the JSONBAgg aggregate function to return only the list of JSON data objects """ - role = obj.role - # Device type and location assignment is relevant only for Devices device_type = getattr(obj, 'device_type', None) location = getattr(obj, 'location', None) @@ -44,13 +42,16 @@ class ConfigContextQuerySet(RestrictedQuerySet): sitegroup = getattr(obj.site, 'group', None) sitegroups = sitegroup.get_ancestors(include_self=True) if sitegroup else [] + # Match against the directly assigned role as well as any parent roles. + device_roles = obj.role.get_ancestors(include_self=True) if obj.role else [] + queryset = self.filter( Q(regions__in=regions) | Q(regions=None), Q(site_groups__in=sitegroups) | Q(site_groups=None), Q(sites=obj.site) | Q(sites=None), Q(locations=location) | Q(locations=None), Q(device_types=device_type) | Q(device_types=None), - Q(roles=role) | Q(roles=None), + Q(roles__in=device_roles) | Q(roles=None), Q(platforms=obj.platform) | Q(platforms=None), Q(cluster_types=cluster_type) | Q(cluster_types=None), Q(cluster_groups=cluster_group) | Q(cluster_groups=None), @@ -107,6 +108,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet): Q(clusters=OuterRef('cluster')) | Q(clusters=None), Q(tenant_groups=OuterRef('tenant__group')) | Q(tenant_groups=None), Q(tenants=OuterRef('tenant')) | Q(tenants=None), + Q(sites=OuterRef('site')) | Q(sites=None), Q( tags__pk__in=Subquery( TaggedItem.objects.filter( @@ -128,9 +130,7 @@ class ConfigContextModelQuerySet(RestrictedQuerySet): base_query.add(Q(locations=None), Q.AND) base_query.add(Q(device_types=None), Q.AND) - base_query.add((Q(roles=OuterRef('role')) | Q(roles=None)), Q.AND) - base_query.add((Q(sites=OuterRef('site')) | Q(sites=None)), Q.AND) - + # MPTT-based filters base_query.add( (Q( regions__tree_id=OuterRef('site__region__tree_id'), @@ -140,7 +140,6 @@ class ConfigContextModelQuerySet(RestrictedQuerySet): ) | Q(regions=None)), Q.AND ) - base_query.add( (Q( site_groups__tree_id=OuterRef('site__group__tree_id'), @@ -150,6 +149,15 @@ class ConfigContextModelQuerySet(RestrictedQuerySet): ) | Q(site_groups=None)), Q.AND ) + base_query.add( + (Q( + roles__tree_id=OuterRef('role__tree_id'), + roles__level__lte=OuterRef('role__level'), + roles__lft__lte=OuterRef('role__lft'), + roles__rght__gte=OuterRef('role__rght'), + ) | Q(roles=None)), + Q.AND + ) return base_query diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 803590cf9..f96066fb1 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -2,10 +2,12 @@ import inspect import json import logging import os +import re import yaml from django import forms from django.conf import settings +from django.core.files.storage import storages from django.core.validators import RegexValidator from django.utils import timezone from django.utils.functional import classproperty @@ -370,9 +372,46 @@ class BaseScript: def filename(self): return inspect.getfile(self.__class__) + def findsource(self, object): + storage = storages.create_storage(storages.backends["scripts"]) + with storage.open(os.path.basename(self.filename), 'r') as f: + data = f.read() + + # Break the source code into lines + lines = [line + '\n' for line in data.splitlines()] + + # Find the class definition + name = object.__name__ + pat = re.compile(r'^(\s*)class\s*' + name + r'\b') + # use the class definition with the least indentation + candidates = [] + for i in range(len(lines)): + match = pat.match(lines[i]) + if match: + if lines[i][0] == 'c': + return lines, i + + candidates.append((match.group(1), i)) + if not candidates: + raise OSError('could not find class definition') + + # Sort the candidates by whitespace, and by line number + candidates.sort() + return lines, candidates[0][1] + @property def source(self): - return inspect.getsource(self.__class__) + # Can't use inspect.getsource() as it uses os to get the file + # inspect uses ast, but that is overkill for this as we only do + # classes. + object = self.__class__ + + try: + lines, lnum = self.findsource(object) + lines = inspect.getblock(lines[lnum:]) + return ''.join(lines) + except OSError: + return '' @classmethod def _get_vars(cls): @@ -528,6 +567,11 @@ class BaseScript: """ Return data from a YAML file """ + # TODO: DEPRECATED: Remove this method in v4.4 + self._log( + _("load_yaml is deprecated and will be removed in v4.4"), + level=LogLevelChoices.LOG_WARNING + ) file_path = os.path.join(settings.SCRIPTS_ROOT, filename) with open(file_path, 'r') as datafile: data = yaml.load(datafile, Loader=yaml.SafeLoader) @@ -538,6 +582,11 @@ class BaseScript: """ Return data from a JSON file """ + # TODO: DEPRECATED: Remove this method in v4.4 + self._log( + _("load_json is deprecated and will be removed in v4.4"), + level=LogLevelChoices.LOG_WARNING + ) file_path = os.path.join(settings.SCRIPTS_ROOT, filename) with open(file_path, 'r') as datafile: data = json.load(datafile) @@ -553,7 +602,6 @@ class BaseScript: Run the report and save its results. Each test method will be executed in order. """ self.logger.info("Running report") - try: for test_name in self.tests: self._current_test = test_name diff --git a/netbox/extras/storage.py b/netbox/extras/storage.py new file mode 100644 index 000000000..ede4fac7f --- /dev/null +++ b/netbox/extras/storage.py @@ -0,0 +1,14 @@ +from django.conf import settings +from django.core.files.storage import FileSystemStorage +from django.utils.functional import cached_property + + +class ScriptFileSystemStorage(FileSystemStorage): + """ + Custom storage for scripts - for django-storages as the default one will + go off media-root and raise security errors as the scripts can be outside + the media-root directory. + """ + @cached_property + def base_location(self): + return settings.SCRIPTS_ROOT diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 805fa5621..e6f488fde 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -30,6 +30,7 @@ __all__ = ( 'ScriptResultsTable', 'ScriptJobTable', 'SubscriptionTable', + 'TableConfigTable', 'TaggedItemTable', 'TagTable', 'WebhookTable', @@ -186,6 +187,15 @@ class ExportTemplateTable(NetBoxTable): object_types = columns.ContentTypesColumn( verbose_name=_('Object Types'), ) + mime_type = tables.Column( + verbose_name=_('MIME Type') + ) + file_name = tables.Column( + verbose_name=_('File Name'), + ) + file_extension = tables.Column( + verbose_name=_('File Extension'), + ) as_attachment = columns.BooleanColumn( verbose_name=_('As Attachment'), false_mark=None @@ -206,11 +216,12 @@ class ExportTemplateTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = ExportTemplate fields = ( - 'pk', 'id', 'name', 'object_types', 'description', 'mime_type', 'file_extension', 'as_attachment', - 'data_source', 'data_file', 'data_synced', 'created', 'last_updated', + 'pk', 'id', 'name', 'object_types', 'description', 'mime_type', 'file_name', 'file_extension', + 'as_attachment', 'data_source', 'data_file', 'data_synced', 'created', 'last_updated', ) default_columns = ( - 'pk', 'name', 'object_types', 'description', 'mime_type', 'file_extension', 'as_attachment', 'is_synced', + 'pk', 'name', 'object_types', 'description', 'mime_type', 'file_name', 'file_extension', + 'as_attachment', 'is_synced', ) @@ -274,6 +285,36 @@ class SavedFilterTable(NetBoxTable): ) +class TableConfigTable(NetBoxTable): + name = tables.Column( + verbose_name=_('Name'), + linkify=True + ) + object_type = columns.ContentTypeColumn( + verbose_name=_('Object Type'), + ) + table = tables.Column( + verbose_name=_('Table Name') + ) + enabled = columns.BooleanColumn( + verbose_name=_('Enabled'), + ) + shared = columns.BooleanColumn( + verbose_name=_('Shared'), + false_mark=None + ) + + class Meta(NetBoxTable.Meta): + model = TableConfig + fields = ( + 'pk', 'id', 'name', 'object_type', 'table', 'description', 'user', 'weight', 'enabled', 'shared', 'created', + 'last_updated', + ) + default_columns = ( + 'pk', 'name', 'object_type', 'table', 'user', 'description', 'enabled', 'shared', + ) + + class BookmarkTable(NetBoxTable): object_type = columns.ContentTypeColumn( verbose_name=_('Object Types'), @@ -452,8 +493,8 @@ class TagTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Tag fields = ( - 'pk', 'id', 'name', 'items', 'slug', 'color', 'description', 'object_types', 'created', 'last_updated', - 'actions', + 'pk', 'id', 'name', 'items', 'slug', 'color', 'weight', 'description', 'object_types', + 'created', 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'items', 'slug', 'color', 'description') @@ -532,6 +573,19 @@ class ConfigTemplateTable(NetBoxTable): orderable=False, verbose_name=_('Synced') ) + mime_type = tables.Column( + verbose_name=_('MIME Type') + ) + file_name = tables.Column( + verbose_name=_('File Name'), + ) + file_extension = tables.Column( + verbose_name=_('File Extension'), + ) + as_attachment = columns.BooleanColumn( + verbose_name=_('As Attachment'), + false_mark=None + ) tags = columns.TagColumn( url_name='extras:configtemplate_list' ) @@ -559,8 +613,9 @@ class ConfigTemplateTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = ConfigTemplate fields = ( - 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'role_count', - 'platform_count', 'device_count', 'vm_count', 'created', 'last_updated', 'tags', + 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'as_attachment', + 'mime_type', 'file_name', 'file_extension', 'role_count', 'platform_count', 'device_count', + 'vm_count', 'created', 'last_updated', 'tags', ) default_columns = ( 'pk', 'name', 'description', 'is_synced', 'device_count', 'vm_count', diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 63baf44d3..6e3fb37fc 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -479,6 +479,7 @@ class ExportTemplateTest(APIViewTestCases.APIViewTestCase): 'object_types': ['dcim.device'], 'name': 'Test Export Template 6', 'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', + 'file_name': 'test_export_template_6', }, ] bulk_update_data = { @@ -494,7 +495,9 @@ class ExportTemplateTest(APIViewTestCases.APIViewTestCase): ), ExportTemplate( name='Export Template 2', - template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}' + template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}', + file_name='export_template_2', + file_extension='test', ), ExportTemplate( name='Export Template 3', @@ -502,8 +505,10 @@ class ExportTemplateTest(APIViewTestCases.APIViewTestCase): ), ) ExportTemplate.objects.bulk_create(export_templates) + + device_object_type = ObjectType.objects.get_for_model(Device) for et in export_templates: - et.object_types.set([ObjectType.objects.get_for_model(Device)]) + et.object_types.set([device_object_type]) class TagTest(APIViewTestCases.APIViewTestCase): @@ -513,6 +518,7 @@ class TagTest(APIViewTestCases.APIViewTestCase): { 'name': 'Tag 4', 'slug': 'tag-4', + 'weight': 1000, }, { 'name': 'Tag 5', @@ -527,6 +533,24 @@ class TagTest(APIViewTestCases.APIViewTestCase): 'description': 'New description', } + @classmethod + def setUpTestData(cls): + + tags = ( + Tag(name='Tag 1', slug='tag-1'), + Tag(name='Tag 2', slug='tag-2'), + Tag(name='Tag 3', slug='tag-3', weight=26), + ) + Tag.objects.bulk_create(tags) + + +class TaggedItemTest( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase +): + model = TaggedItem + brief_fields = ['display', 'id', 'object', 'object_id', 'object_type', 'tag', 'url'] + @classmethod def setUpTestData(cls): @@ -537,6 +561,16 @@ class TagTest(APIViewTestCases.APIViewTestCase): ) Tag.objects.bulk_create(tags) + sites = ( + Site(name='Site 1', slug='site-1'), + Site(name='Site 2', slug='site-2'), + Site(name='Site 3', slug='site-3'), + ) + Site.objects.bulk_create(sites) + sites[0].tags.set([tags[0], tags[1]]) + sites[1].tags.set([tags[1], tags[2]]) + sites[2].tags.set([tags[2], tags[0]]) + # TODO: Standardize to APIViewTestCase (needs create & update tests) class ImageAttachmentTest( @@ -721,6 +755,10 @@ class ConfigTemplateTest(APIViewTestCases.APIViewTestCase): { 'name': 'Config Template 4', 'template_code': 'Foo: {{ foo }}', + 'mime_type': 'text/plain', + 'file_name': 'output4', + 'file_extension': 'txt', + 'as_attachment': True, }, { 'name': 'Config Template 5', @@ -744,7 +782,7 @@ class ConfigTemplateTest(APIViewTestCases.APIViewTestCase): ), ConfigTemplate( name='Config Template 2', - template_code='Bar: {{ bar }}' + template_code='Bar: {{ bar }}', ), ConfigTemplate( name='Config Template 3', diff --git a/netbox/extras/tests/test_filtersets.py b/netbox/extras/tests/test_filtersets.py index 03d8508af..f9147a30c 100644 --- a/netbox/extras/tests/test_filtersets.py +++ b/netbox/extras/tests/test_filtersets.py @@ -616,16 +616,39 @@ class BookmarkTestCase(TestCase, BaseFilterSetTests): class ExportTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = ExportTemplate.objects.all() filterset = ExportTemplateFilterSet - ignore_fields = ('template_code', 'data_path') + ignore_fields = ('template_code', 'environment_params', 'data_path') @classmethod def setUpTestData(cls): object_types = ObjectType.objects.filter(model__in=['site', 'rack', 'device']) export_templates = ( - ExportTemplate(name='Export Template 1', template_code='TESTING', description='foobar1'), - ExportTemplate(name='Export Template 2', template_code='TESTING', description='foobar2'), - ExportTemplate(name='Export Template 3', template_code='TESTING'), + ExportTemplate( + name='Export Template 1', + template_code='TESTING', + description='foobar1', + mime_type='text/foo', + file_name='foo', + file_extension='foo', + as_attachment=True, + ), + ExportTemplate( + name='Export Template 2', + template_code='TESTING', + description='foobar2', + mime_type='text/bar', + file_name='bar', + file_extension='bar', + as_attachment=True, + ), + ExportTemplate( + name='Export Template 3', + template_code='TESTING', + mime_type='text/baz', + file_name='baz', + file_extension='baz', + as_attachment=False, + ), ) ExportTemplate.objects.bulk_create(export_templates) for i, et in enumerate(export_templates): @@ -649,6 +672,22 @@ class ExportTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_mime_type(self): + params = {'mime_type': ['text/foo', 'text/bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_file_name(self): + params = {'file_name': ['foo', 'bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_file_extension(self): + params = {'file_extension': ['foo', 'bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_as_attachment(self): + params = {'as_attachment': True} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class ImageAttachmentTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = ImageAttachment.objects.all() @@ -884,7 +923,8 @@ class ConfigContextTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(device_roles) + for device_role in device_roles: + device_role.save() platforms = ( Platform(name='Platform 1', slug='platform-1'), @@ -1067,9 +1107,32 @@ class ConfigTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): @classmethod def setUpTestData(cls): config_templates = ( - ConfigTemplate(name='Config Template 1', template_code='TESTING', description='foobar1'), - ConfigTemplate(name='Config Template 2', template_code='TESTING', description='foobar2'), - ConfigTemplate(name='Config Template 3', template_code='TESTING'), + ConfigTemplate( + name='Config Template 1', + template_code='TESTING', + description='foobar1', + mime_type='text/foo', + file_name='foo', + file_extension='foo', + as_attachment=True, + ), + ConfigTemplate( + name='Config Template 2', + template_code='TESTING', + description='foobar2', + mime_type='text/bar', + file_name='bar', + file_extension='bar', + as_attachment=True, + ), + ConfigTemplate( + name='Config Template 3', + template_code='TESTING', + mime_type='text/baz', + file_name='baz', + file_extension='baz', + as_attachment=False, + ), ) ConfigTemplate.objects.bulk_create(config_templates) @@ -1085,6 +1148,22 @@ class ConfigTemplateTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_mime_type(self): + params = {'mime_type': ['text/foo', 'text/bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_file_name(self): + params = {'file_name': ['foo', 'bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_file_extension(self): + params = {'file_extension': ['foo', 'bar']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + def test_as_attachment(self): + params = {'as_attachment': True} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class TagTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = Tag.objects.all() @@ -1141,6 +1220,7 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): 'module', 'modulebay', 'moduletype', + 'moduletypeprofile', 'platform', 'powerfeed', 'poweroutlet', @@ -1195,9 +1275,9 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): } tags = ( - Tag(name='Tag 1', slug='tag-1', color='ff0000', description='foobar1'), - Tag(name='Tag 2', slug='tag-2', color='00ff00', description='foobar2'), - Tag(name='Tag 3', slug='tag-3', color='0000ff'), + Tag(name='Tag 1', slug='tag-1', color='ff0000', weight=1000, description='foobar1'), + Tag(name='Tag 2', slug='tag-2', color='00ff00', weight=2000, description='foobar2'), + Tag(name='Tag 3', slug='tag-3', color='0000ff', weight=3000), ) Tag.objects.bulk_create(tags) tags[0].object_types.add(object_types['site']) @@ -1250,6 +1330,66 @@ class TagTestCase(TestCase, ChangeLoggedFilterSetTests): ['Tag 2', 'Tag 3'] ) + def test_weight(self): + params = {'weight': [1000, 2000]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + +class TaggedItemFilterSetTestCase(TestCase): + queryset = TaggedItem.objects.all() + filterset = TaggedItemFilterSet + + @classmethod + def setUpTestData(cls): + tags = ( + Tag(name='Tag 1', slug='tag-1'), + Tag(name='Tag 2', slug='tag-2'), + Tag(name='Tag 3', slug='tag-3'), + ) + Tag.objects.bulk_create(tags) + + sites = ( + Site(name='Site 1', slug='site-1'), + Site(name='Site 2', slug='site-2'), + Site(name='Site 3', slug='site-3'), + ) + Site.objects.bulk_create(sites) + sites[0].tags.add(tags[0]) + sites[1].tags.add(tags[1]) + sites[2].tags.add(tags[2]) + + tenants = ( + Tenant(name='Tenant 1', slug='tenant-1'), + Tenant(name='Tenant 2', slug='tenant-2'), + Tenant(name='Tenant 3', slug='tenant-3'), + ) + Tenant.objects.bulk_create(tenants) + tenants[0].tags.add(tags[0]) + tenants[1].tags.add(tags[1]) + tenants[2].tags.add(tags[2]) + + def test_tag(self): + tags = Tag.objects.all()[:2] + params = {'tag': [tags[0].slug, tags[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + params = {'tag_id': [tags[0].pk, tags[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) + + def test_object_type(self): + object_type = ObjectType.objects.get_for_model(Site) + params = {'object_type': 'dcim.site'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + params = {'object_type_id': [object_type.pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + + def test_object_id(self): + site_ids = Site.objects.values_list('pk', flat=True) + params = { + 'object_type': 'dcim.site', + 'object_id': site_ids[:2], + } + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class ChangeLoggedFilterSetTestCase(TestCase): """ diff --git a/netbox/extras/tests/test_models.py b/netbox/extras/tests/test_models.py index 34882537d..089e47c02 100644 --- a/netbox/extras/tests/test_models.py +++ b/netbox/extras/tests/test_models.py @@ -11,6 +11,40 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMac class TagTest(TestCase): + def test_default_ordering_weight_then_name_is_set(self): + Tag.objects.create(name='Tag 1', slug='tag-1', weight=3000) + Tag.objects.create(name='Tag 2', slug='tag-2') # Default: 1000 + Tag.objects.create(name='Tag 3', slug='tag-3', weight=2000) + Tag.objects.create(name='Tag 4', slug='tag-4', weight=2000) + + tags = Tag.objects.all() + + self.assertEqual(tags[0].slug, 'tag-2') + self.assertEqual(tags[1].slug, 'tag-3') + self.assertEqual(tags[2].slug, 'tag-4') + self.assertEqual(tags[3].slug, 'tag-1') + + def test_tag_related_manager_ordering_weight_then_name(self): + tags = [ + Tag.objects.create(name='Tag 1', slug='tag-1', weight=3000), + Tag.objects.create(name='Tag 2', slug='tag-2'), # Default: 1000 + Tag.objects.create(name='Tag 3', slug='tag-3', weight=2000), + Tag.objects.create(name='Tag 4', slug='tag-4', weight=2000), + ] + + site = Site.objects.create(name='Site 1') + for tag in tags: + site.tags.add(tag) + site.save() + + site = Site.objects.first() + tags = site.tags.all() + + self.assertEqual(tags[0].slug, 'tag-2') + self.assertEqual(tags[1].slug, 'tag-3') + self.assertEqual(tags[2].slug, 'tag-4') + self.assertEqual(tags[3].slug, 'tag-1') + def test_create_tag_unicode(self): tag = Tag(name='Testing Unicode: 台灣') tag.save() diff --git a/netbox/extras/tests/test_utils.py b/netbox/extras/tests/test_utils.py new file mode 100644 index 000000000..b851acab8 --- /dev/null +++ b/netbox/extras/tests/test_utils.py @@ -0,0 +1,19 @@ +from django.test import TestCase + +from extras.models import ExportTemplate +from extras.utils import filename_from_model +from tenancy.models import ContactGroup, TenantGroup +from wireless.models import WirelessLANGroup + + +class FilenameFromModelTests(TestCase): + def test_expected_output(self): + cases = ( + (ExportTemplate, 'netbox_export_templates'), + (ContactGroup, 'netbox_contact_groups'), + (TenantGroup, 'netbox_tenant_groups'), + (WirelessLANGroup, 'netbox_wireless_lan_groups'), + ) + + for model, expected in cases: + self.assertEqual(filename_from_model(model), expected) diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py index 5d82fae4c..6378b29b8 100644 --- a/netbox/extras/tests/test_views.py +++ b/netbox/extras/tests/test_views.py @@ -301,11 +301,14 @@ class ExportTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase): def setUpTestData(cls): site_type = ObjectType.objects.get_for_model(Site) TEMPLATE_CODE = """{% for object in queryset %}{{ object }}{% endfor %}""" + ENVIRONMENT_PARAMS = """{"trim_blocks": true}""" export_templates = ( ExportTemplate(name='Export Template 1', template_code=TEMPLATE_CODE), - ExportTemplate(name='Export Template 2', template_code=TEMPLATE_CODE), - ExportTemplate(name='Export Template 3', template_code=TEMPLATE_CODE), + ExportTemplate( + name='Export Template 2', template_code=TEMPLATE_CODE, environment_params={"trim_blocks": True} + ), + ExportTemplate(name='Export Template 3', template_code=TEMPLATE_CODE, file_name='export_template_3') ) ExportTemplate.objects.bulk_create(export_templates) for et in export_templates: @@ -315,13 +318,15 @@ class ExportTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'name': 'Export Template X', 'object_types': [site_type.pk], 'template_code': TEMPLATE_CODE, + 'environment_params': ENVIRONMENT_PARAMS, + 'file_name': 'template_x', } cls.csv_data = ( - "name,object_types,template_code", - f"Export Template 4,dcim.site,{TEMPLATE_CODE}", - f"Export Template 5,dcim.site,{TEMPLATE_CODE}", - f"Export Template 6,dcim.site,{TEMPLATE_CODE}", + "name,object_types,template_code,file_name", + f"Export Template 4,dcim.site,{TEMPLATE_CODE},", + f"Export Template 5,dcim.site,{TEMPLATE_CODE},template_5", + f"Export Template 6,dcim.site,{TEMPLATE_CODE},", ) cls.csv_update_data = ( @@ -441,8 +446,8 @@ class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase): tags = ( Tag(name='Tag 1', slug='tag-1'), - Tag(name='Tag 2', slug='tag-2'), - Tag(name='Tag 3', slug='tag-3'), + Tag(name='Tag 2', slug='tag-2', weight=1), + Tag(name='Tag 3', slug='tag-3', weight=32767), ) Tag.objects.bulk_create(tags) @@ -451,13 +456,14 @@ class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'slug': 'tag-x', 'color': 'c0c0c0', 'comments': 'Some comments', + 'weight': 11, } cls.csv_data = ( - "name,slug,color,description", - "Tag 4,tag-4,ff0000,Fourth tag", - "Tag 5,tag-5,00ff00,Fifth tag", - "Tag 6,tag-6,0000ff,Sixth tag", + "name,slug,color,description,weight", + "Tag 4,tag-4,ff0000,Fourth tag,0", + "Tag 5,tag-5,00ff00,Fifth tag,1111", + "Tag 6,tag-6,0000ff,Sixth tag,0", ) cls.csv_update_data = ( @@ -535,11 +541,23 @@ class ConfigTemplateTestCase( @classmethod def setUpTestData(cls): TEMPLATE_CODE = """Foo: {{ foo }}""" + ENVIRONMENT_PARAMS = """{"trim_blocks": true}""" config_templates = ( - ConfigTemplate(name='Config Template 1', template_code=TEMPLATE_CODE), - ConfigTemplate(name='Config Template 2', template_code=TEMPLATE_CODE), - ConfigTemplate(name='Config Template 3', template_code=TEMPLATE_CODE), + ConfigTemplate( + name='Config Template 1', + template_code=TEMPLATE_CODE) + , + ConfigTemplate( + name='Config Template 2', + template_code=TEMPLATE_CODE, + environment_params={"trim_blocks": True}, + ), + ConfigTemplate( + name='Config Template 3', + template_code=TEMPLATE_CODE, + file_name='config_template_3', + ), ) ConfigTemplate.objects.bulk_create(config_templates) @@ -547,6 +565,8 @@ class ConfigTemplateTestCase( 'name': 'Config Template X', 'description': 'Config template', 'template_code': TEMPLATE_CODE, + 'environment_params': ENVIRONMENT_PARAMS, + 'file_name': 'config_x', } cls.csv_update_data = ( @@ -558,6 +578,10 @@ class ConfigTemplateTestCase( cls.bulk_edit_data = { 'description': 'New description', + 'mime_type': 'text/html', + 'file_name': 'output', + 'file_extension': 'html', + 'as_attachment': True, } diff --git a/netbox/extras/urls.py b/netbox/extras/urls.py index f470f72b1..ca07ba903 100644 --- a/netbox/extras/urls.py +++ b/netbox/extras/urls.py @@ -19,6 +19,9 @@ urlpatterns = [ path('export-templates/', include(get_model_urls('extras', 'exporttemplate', detail=False))), path('export-templates//', include(get_model_urls('extras', 'exporttemplate'))), + path('table-configs/', include(get_model_urls('extras', 'tableconfig', detail=False))), + path('table-configs//', include(get_model_urls('extras', 'tableconfig'))), + path('saved-filters/', include(get_model_urls('extras', 'savedfilter', detail=False))), path('saved-filters//', include(get_model_urls('extras', 'savedfilter'))), diff --git a/netbox/extras/utils.py b/netbox/extras/utils.py index efe7ada5b..c9f554d22 100644 --- a/netbox/extras/utils.py +++ b/netbox/extras/utils.py @@ -1,12 +1,15 @@ import importlib from django.core.exceptions import ImproperlyConfigured +from django.db import models +from django.db.models import Q from taggit.managers import _TaggableManager from netbox.context import current_request from .validators import CustomValidator __all__ = ( + 'SharedObjectViewMixin', 'image_upload', 'is_report', 'is_script', @@ -15,6 +18,39 @@ __all__ = ( ) +class SharedObjectViewMixin: + + def get_queryset(self, request): + """ + Return only shared objects, or those owned by the current user, unless this is a superuser. + """ + queryset = super().get_queryset(request) + if request.user.is_superuser: + return queryset + if request.user.is_anonymous: + return queryset.filter(shared=True) + return queryset.filter( + Q(shared=True) | Q(user=request.user) + ) + + +def filename_from_model(model: models.Model) -> str: + """Standardises how we generate filenames from model class for exports""" + base = model._meta.verbose_name_plural.lower().replace(' ', '_') + return f'netbox_{base}' + + +def filename_from_object(context: dict) -> str: + """Standardises how we generate filenames from model class for exports""" + if 'device' in context: + base = f"{context['device'].name or 'config'}" + elif 'virtualmachine' in context: + base = f"{context['virtualmachine'].name or 'config'}" + else: + base = 'config' + return base + + def is_taggable(obj): """ Return True if the instance can have Tags assigned to it; False otherwise. diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 72ea72574..ae9337779 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -13,12 +13,12 @@ from django.views.generic import View from jinja2.exceptions import TemplateError from core.choices import ManagedFileRootPathChoices -from core.forms import ManagedFileForm from core.models import Job from dcim.models import Device, DeviceRole, Platform from extras.choices import LogLevelChoices from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm from extras.dashboard.utils import get_widget_class +from extras.utils import SharedObjectViewMixin from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.views import generic from netbox.views.generic.mixins import TableMixin @@ -286,39 +286,22 @@ class ExportTemplateBulkSyncDataView(generic.BulkSyncDataView): # Saved filters # -class SavedFilterMixin: - - def get_queryset(self, request): - """ - Return only shared SavedFilters, or those owned by the current user, unless - this is a superuser. - """ - queryset = SavedFilter.objects.all() - user = request.user - if user.is_superuser: - return queryset - if user.is_anonymous: - return queryset.filter(shared=True) - return queryset.filter( - Q(shared=True) | Q(user=user) - ) - - @register_model_view(SavedFilter, 'list', path='', detail=False) -class SavedFilterListView(SavedFilterMixin, generic.ObjectListView): +class SavedFilterListView(SharedObjectViewMixin, generic.ObjectListView): + queryset = SavedFilter.objects.all() filterset = filtersets.SavedFilterFilterSet filterset_form = forms.SavedFilterFilterForm table = tables.SavedFilterTable @register_model_view(SavedFilter) -class SavedFilterView(SavedFilterMixin, generic.ObjectView): +class SavedFilterView(SharedObjectViewMixin, generic.ObjectView): queryset = SavedFilter.objects.all() @register_model_view(SavedFilter, 'add', detail=False) @register_model_view(SavedFilter, 'edit') -class SavedFilterEditView(SavedFilterMixin, generic.ObjectEditView): +class SavedFilterEditView(SharedObjectViewMixin, generic.ObjectEditView): queryset = SavedFilter.objects.all() form = forms.SavedFilterForm @@ -329,18 +312,18 @@ class SavedFilterEditView(SavedFilterMixin, generic.ObjectEditView): @register_model_view(SavedFilter, 'delete') -class SavedFilterDeleteView(SavedFilterMixin, generic.ObjectDeleteView): +class SavedFilterDeleteView(SharedObjectViewMixin, generic.ObjectDeleteView): queryset = SavedFilter.objects.all() @register_model_view(SavedFilter, 'bulk_import', path='import', detail=False) -class SavedFilterBulkImportView(SavedFilterMixin, generic.BulkImportView): +class SavedFilterBulkImportView(SharedObjectViewMixin, generic.BulkImportView): queryset = SavedFilter.objects.all() model_form = forms.SavedFilterImportForm @register_model_view(SavedFilter, 'bulk_edit', path='edit', detail=False) -class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView): +class SavedFilterBulkEditView(SharedObjectViewMixin, generic.BulkEditView): queryset = SavedFilter.objects.all() filterset = filtersets.SavedFilterFilterSet table = tables.SavedFilterTable @@ -348,12 +331,71 @@ class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView): @register_model_view(SavedFilter, 'bulk_delete', path='delete', detail=False) -class SavedFilterBulkDeleteView(SavedFilterMixin, generic.BulkDeleteView): +class SavedFilterBulkDeleteView(SharedObjectViewMixin, generic.BulkDeleteView): queryset = SavedFilter.objects.all() filterset = filtersets.SavedFilterFilterSet table = tables.SavedFilterTable +# +# Table configs +# + +@register_model_view(TableConfig, 'list', path='', detail=False) +class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView): + queryset = TableConfig.objects.all() + filterset = filtersets.TableConfigFilterSet + filterset_form = forms.TableConfigFilterForm + table = tables.TableConfigTable + actions = { + 'export': {'view'}, + } + + +@register_model_view(TableConfig) +class TableConfigView(SharedObjectViewMixin, generic.ObjectView): + queryset = TableConfig.objects.all() + + def get_extra_context(self, request, instance): + table = instance.table_class([]) + return { + 'columns': dict(table.columns.items()), + } + + +@register_model_view(TableConfig, 'add', detail=False) +@register_model_view(TableConfig, 'edit') +class TableConfigEditView(SharedObjectViewMixin, generic.ObjectEditView): + queryset = TableConfig.objects.all() + form = forms.TableConfigForm + template_name = 'extras/tableconfig_edit.html' + + def alter_object(self, obj, request, url_args, url_kwargs): + if not obj.pk: + obj.user = request.user + return obj + + +@register_model_view(TableConfig, 'delete') +class TableConfigDeleteView(SharedObjectViewMixin, generic.ObjectDeleteView): + queryset = TableConfig.objects.all() + + +@register_model_view(TableConfig, 'bulk_edit', path='edit', detail=False) +class TableConfigBulkEditView(SharedObjectViewMixin, generic.BulkEditView): + queryset = TableConfig.objects.all() + filterset = filtersets.TableConfigFilterSet + table = tables.TableConfigTable + form = forms.TableConfigBulkEditForm + + +@register_model_view(TableConfig, 'bulk_delete', path='delete', detail=False) +class TableConfigBulkDeleteView(SharedObjectViewMixin, generic.BulkDeleteView): + queryset = TableConfig.objects.all() + filterset = filtersets.TableConfigFilterSet + table = tables.TableConfigTable + + # # Bookmarks # @@ -1218,7 +1260,7 @@ class DashboardWidgetDeleteView(LoginRequiredMixin, View): @register_model_view(ScriptModule, 'edit') class ScriptModuleCreateView(generic.ObjectEditView): queryset = ScriptModule.objects.all() - form = ManagedFileForm + form = forms.ScriptFileForm def alter_object(self, obj, *args, **kwargs): obj.file_root = ManagedFileRootPathChoices.SCRIPTS diff --git a/netbox/extras/webhooks.py b/netbox/extras/webhooks.py index 889c97ac2..368075217 100644 --- a/netbox/extras/webhooks.py +++ b/netbox/extras/webhooks.py @@ -3,10 +3,10 @@ import hmac import logging import requests -from django.conf import settings from django_rq import job from jinja2.exceptions import TemplateError +from utilities.proxy import resolve_proxies from .constants import WEBHOOK_EVENT_TYPES logger = logging.getLogger('netbox.webhooks') @@ -63,9 +63,10 @@ def send_webhook(event_rule, model_name, event_type, data, timestamp, username, raise e # Prepare the HTTP request + url = webhook.render_payload_url(context) params = { 'method': webhook.http_method, - 'url': webhook.render_payload_url(context), + 'url': url, 'headers': headers, 'data': body.encode('utf8'), } @@ -88,7 +89,8 @@ def send_webhook(event_rule, model_name, event_type, data, timestamp, username, session.verify = webhook.ssl_verification if webhook.ca_file_path: session.verify = webhook.ca_file_path - response = session.send(prepared_request, proxies=settings.HTTP_PROXIES) + proxies = resolve_proxies(url=url, context={'client': webhook}) + response = session.send(prepared_request, proxies=proxies) if 200 <= response.status_code <= 299: logger.info(f"Request succeeded; response status {response.status_code}") diff --git a/netbox/ipam/api/serializers_/ip.py b/netbox/ipam/api/serializers_/ip.py index bfc7ac546..6f815b5ce 100644 --- a/netbox/ipam/api/serializers_/ip.py +++ b/netbox/ipam/api/serializers_/ip.py @@ -147,7 +147,8 @@ class IPRangeSerializer(NetBoxModelSerializer): fields = [ 'id', 'url', 'display_url', 'display', 'family', 'start_address', 'end_address', 'size', 'vrf', 'tenant', 'status', 'role', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', - 'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'mark_populated', 'mark_utilized', 'description', 'comments', 'tags', 'custom_fields', 'created', + 'last_updated', ] brief_fields = ('id', 'url', 'display', 'family', 'start_address', 'end_address', 'description') diff --git a/netbox/ipam/api/serializers_/services.py b/netbox/ipam/api/serializers_/services.py index 61b330d01..c7c1bb136 100644 --- a/netbox/ipam/api/serializers_/services.py +++ b/netbox/ipam/api/serializers_/services.py @@ -1,9 +1,13 @@ -from dcim.api.serializers_.devices import DeviceSerializer +from django.contrib.contenttypes.models import ContentType +from drf_spectacular.utils import extend_schema_field +from rest_framework import serializers + from ipam.choices import * +from ipam.constants import SERVICE_ASSIGNMENT_MODELS from ipam.models import IPAddress, Service, ServiceTemplate -from netbox.api.fields import ChoiceField, SerializedPKRelatedField +from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField from netbox.api.serializers import NetBoxModelSerializer -from virtualization.api.serializers_.virtualmachines import VirtualMachineSerializer +from utilities.api import get_serializer_for_model from .ip import IPAddressSerializer __all__ = ( @@ -25,8 +29,6 @@ class ServiceTemplateSerializer(NetBoxModelSerializer): class ServiceSerializer(NetBoxModelSerializer): - device = DeviceSerializer(nested=True, required=False, allow_null=True) - virtual_machine = VirtualMachineSerializer(nested=True, required=False, allow_null=True) protocol = ChoiceField(choices=ServiceProtocolChoices, required=False) ipaddresses = SerializedPKRelatedField( queryset=IPAddress.objects.all(), @@ -35,11 +37,24 @@ class ServiceSerializer(NetBoxModelSerializer): required=False, many=True ) + parent_object_type = ContentTypeField( + queryset=ContentType.objects.filter(SERVICE_ASSIGNMENT_MODELS) + ) + parent = serializers.SerializerMethodField(read_only=True) class Meta: model = Service fields = [ - 'id', 'url', 'display_url', 'display', 'device', 'virtual_machine', 'name', 'protocol', 'ports', - 'ipaddresses', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'url', 'display_url', 'display', 'parent_object_type', 'parent_object_id', 'parent', 'name', + 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags', 'custom_fields', + 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'protocol', 'ports', 'description') + + @extend_schema_field(serializers.JSONField(allow_null=True)) + def get_parent(self, obj): + if obj.parent is None: + return None + serializer = get_serializer_for_model(obj.parent) + context = {'request': self.context['request']} + return serializer(obj.parent, nested=True, context=context).data diff --git a/netbox/ipam/api/serializers_/vlans.py b/netbox/ipam/api/serializers_/vlans.py index 9b5501dc5..a6f428343 100644 --- a/netbox/ipam/api/serializers_/vlans.py +++ b/netbox/ipam/api/serializers_/vlans.py @@ -37,6 +37,7 @@ class VLANGroupSerializer(NetBoxModelSerializer): scope = serializers.SerializerMethodField(read_only=True) vid_ranges = IntegerRangeSerializer(many=True, required=False) utilization = serializers.CharField(read_only=True) + tenant = TenantSerializer(nested=True, required=False, allow_null=True) # Related object counts vlan_count = RelatedObjectCountField('vlans') @@ -45,7 +46,7 @@ class VLANGroupSerializer(NetBoxModelSerializer): model = VLANGroup fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'vid_ranges', - 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization' + 'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization' ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count') validators = [] diff --git a/netbox/ipam/constants.py b/netbox/ipam/constants.py index 6dffd3287..947f1adea 100644 --- a/netbox/ipam/constants.py +++ b/netbox/ipam/constants.py @@ -83,6 +83,12 @@ VLANGROUP_SCOPE_TYPES = ( # Services # +SERVICE_ASSIGNMENT_MODELS = Q( + Q(app_label='dcim', model='device') | + Q(app_label='ipam', model='fhrpgroup') | + Q(app_label='virtualization', model='virtualmachine') +) + # 16-bit port number SERVICE_PORT_MIN = 1 SERVICE_PORT_MAX = 65535 diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index 63131b125..087bcc3b0 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -504,7 +504,7 @@ class IPRangeFilterSet(TenancyFilterSet, NetBoxModelFilterSet, ContactModelFilte class Meta: model = IPRange - fields = ('id', 'mark_utilized', 'size', 'description') + fields = ('id', 'mark_populated', 'mark_utilized', 'size', 'description') def search(self, queryset, name, value): if not value.strip(): @@ -883,7 +883,7 @@ class FHRPGroupAssignmentFilterSet(ChangeLoggedModelFilterSet): ) -class VLANGroupFilterSet(OrganizationalModelFilterSet): +class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet): scope_type = ContentTypeFilter() region = django_filters.NumberFilter( method='filter_scope' @@ -1163,25 +1163,35 @@ class ServiceTemplateFilterSet(NetBoxModelFilterSet): class ServiceFilterSet(ContactModelFilterSet, NetBoxModelFilterSet): - device_id = django_filters.ModelMultipleChoiceFilter( - queryset=Device.objects.all(), - label=_('Device (ID)'), - ) - device = django_filters.ModelMultipleChoiceFilter( - field_name='device__name', - queryset=Device.objects.all(), - to_field_name='name', + device = MultiValueCharFilter( + method='filter_device', + field_name='name', label=_('Device (name)'), ) - virtual_machine_id = django_filters.ModelMultipleChoiceFilter( - queryset=VirtualMachine.objects.all(), + device_id = MultiValueNumberFilter( + method='filter_device', + field_name='pk', + label=_('Device (ID)'), + ) + virtual_machine = MultiValueCharFilter( + method='filter_virtual_machine', + field_name='name', + label=_('Virtual machine (name)'), + ) + virtual_machine_id = MultiValueNumberFilter( + method='filter_virtual_machine', + field_name='pk', label=_('Virtual machine (ID)'), ) - virtual_machine = django_filters.ModelMultipleChoiceFilter( - field_name='virtual_machine__name', - queryset=VirtualMachine.objects.all(), - to_field_name='name', - label=_('Virtual machine (name)'), + fhrpgroup = MultiValueCharFilter( + method='filter_fhrp_group', + field_name='name', + label=_('FHRP Group (name)'), + ) + fhrpgroup_id = MultiValueNumberFilter( + method='filter_fhrp_group', + field_name='pk', + label=_('FHRP Group (ID)'), ) ip_address_id = django_filters.ModelMultipleChoiceFilter( field_name='ipaddresses', @@ -1201,7 +1211,7 @@ class ServiceFilterSet(ContactModelFilterSet, NetBoxModelFilterSet): class Meta: model = Service - fields = ('id', 'name', 'protocol', 'description') + fields = ('id', 'name', 'protocol', 'description', 'parent_object_type', 'parent_object_id') def search(self, queryset, name, value): if not value.strip(): @@ -1209,6 +1219,33 @@ class ServiceFilterSet(ContactModelFilterSet, NetBoxModelFilterSet): qs_filter = Q(name__icontains=value) | Q(description__icontains=value) return queryset.filter(qs_filter) + def filter_device(self, queryset, name, value): + devices = Device.objects.filter(**{'{}__in'.format(name): value}) + if not devices.exists(): + return queryset.none() + service_ids = [] + for device in devices: + service_ids.extend(device.services.values_list('id', flat=True)) + return queryset.filter(id__in=service_ids) + + def filter_fhrp_group(self, queryset, name, value): + groups = FHRPGroup.objects.filter(**{'{}__in'.format(name): value}) + if not groups.exists(): + return queryset.none() + service_ids = [] + for group in groups: + service_ids.extend(group.services.values_list('id', flat=True)) + return queryset.filter(id__in=service_ids) + + def filter_virtual_machine(self, queryset, name, value): + virtual_machines = VirtualMachine.objects.filter(**{'{}__in'.format(name): value}) + if not virtual_machines.exists(): + return queryset.none() + service_ids = [] + for vm in virtual_machines: + service_ids.extend(vm.services.values_list('id', flat=True)) + return queryset.filter(id__in=service_ids) + class PrimaryIPFilterSet(django_filters.FilterSet): """ diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index 7f3216cfd..864630bd4 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -296,6 +296,11 @@ class IPRangeBulkEditForm(NetBoxModelBulkEditForm): queryset=Role.objects.all(), required=False ) + mark_populated = forms.NullBooleanField( + required=False, + widget=BulkEditNullBooleanSelect(), + label=_('Treat as populated') + ) mark_utilized = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect(), @@ -430,11 +435,17 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): label=_('VLAN ID ranges'), required=False ) + tenant = DynamicModelChoiceField( + label=_('Tenant'), + queryset=Tenant.objects.all(), + required=False + ) model = VLANGroup fieldsets = ( FieldSet('site', 'vid_ranges', 'description'), FieldSet('scope_type', 'scope', name=_('Scope')), + FieldSet('tenant', name=_('Tenancy')), ) nullable_fields = ('description', 'scope') diff --git a/netbox/ipam/forms/bulk_import.py b/netbox/ipam/forms/bulk_import.py index 0fbcd414b..d17944674 100644 --- a/netbox/ipam/forms/bulk_import.py +++ b/netbox/ipam/forms/bulk_import.py @@ -275,8 +275,8 @@ class IPRangeImportForm(NetBoxModelImportForm): class Meta: model = IPRange fields = ( - 'start_address', 'end_address', 'vrf', 'tenant', 'status', 'role', 'mark_utilized', 'description', - 'comments', 'tags', + 'start_address', 'end_address', 'vrf', 'tenant', 'status', 'role', 'mark_populated', 'mark_utilized', + 'description', 'comments', 'tags', ) @@ -454,10 +454,17 @@ class VLANGroupImportForm(NetBoxModelImportForm): vid_ranges = NumericRangeArrayField( required=False ) + tenant = CSVModelChoiceField( + label=_('Tenant'), + queryset=Tenant.objects.all(), + required=False, + to_field_name='name', + help_text=_('Assigned tenant') + ) class Meta: model = VLANGroup - fields = ('name', 'slug', 'scope_type', 'scope_id', 'vid_ranges', 'description', 'tags') + fields = ('name', 'slug', 'scope_type', 'scope_id', 'vid_ranges', 'tenant', 'description', 'tags') labels = { 'scope_id': 'Scope ID', } @@ -552,19 +559,21 @@ class ServiceTemplateImportForm(NetBoxModelImportForm): class ServiceImportForm(NetBoxModelImportForm): - device = CSVModelChoiceField( - label=_('Device'), + parent_object_type = CSVContentTypeField( + queryset=ContentType.objects.filter(SERVICE_ASSIGNMENT_MODELS), + required=True, + label=_('Parent type (app & model)') + ) + parent = CSVModelChoiceField( + label=_('Parent'), queryset=Device.objects.all(), required=False, to_field_name='name', - help_text=_('Required if not assigned to a VM') + help_text=_('Parent object name') ) - virtual_machine = CSVModelChoiceField( - label=_('Virtual machine'), - queryset=VirtualMachine.objects.all(), + parent_object_id = forms.IntegerField( required=False, - to_field_name='name', - help_text=_('Required if not assigned to a device') + help_text=_('Parent object ID'), ) protocol = CSVChoiceField( label=_('Protocol'), @@ -581,15 +590,52 @@ class ServiceImportForm(NetBoxModelImportForm): class Meta: model = Service fields = ( - 'device', 'virtual_machine', 'ipaddresses', 'name', 'protocol', 'ports', 'description', 'comments', 'tags', + 'ipaddresses', 'name', 'protocol', 'ports', 'description', 'comments', 'tags', ) - def clean_ipaddresses(self): - parent = self.cleaned_data.get('device') or self.cleaned_data.get('virtual_machine') - for ip_address in self.cleaned_data['ipaddresses']: - if not ip_address.assigned_object or getattr(ip_address.assigned_object, 'parent_object') != parent: + def __init__(self, data=None, *args, **kwargs): + super().__init__(data, *args, **kwargs) + + # Limit parent queryset by assigned parent object type + if data: + match data.get('parent_object_type'): + case 'dcim.device': + self.fields['parent'].queryset = Device.objects.all() + case 'ipam.fhrpgroup': + self.fields['parent'].queryset = FHRPGroup.objects.all() + case 'virtualization.virtualmachine': + self.fields['parent'].queryset = VirtualMachine.objects.all() + + def save(self, *args, **kwargs): + if (parent := self.cleaned_data.get('parent')): + self.instance.parent = parent + + return super().save(*args, **kwargs) + + def clean(self): + super().clean() + + if (parent_ct := self.cleaned_data.get('parent_object_type')): + if (parent := self.cleaned_data.get('parent')): + self.cleaned_data['parent_object_id'] = parent.pk + elif (parent_id := self.cleaned_data.get('parent_object_id')): + parent = parent_ct.model_class().objects.filter(id=parent_id).first() + self.cleaned_data['parent'] = parent + else: + # If a parent object type is passed and we've made it here, then raise a validation + # error since an associated parent object or parent object id has not been passed raise forms.ValidationError( - _("{ip} is not assigned to this device/VM.").format(ip=ip_address) + _("One of parent or parent_object_id must be included with parent_object_type") ) - return self.cleaned_data['ipaddresses'] + # making sure parent is defined. In cases where an import is resulting in an update, the + # import data might not include the parent object and so the above logic might not be + # triggered + parent = self.cleaned_data.get('parent') + for ip_address in self.cleaned_data.get('ipaddresses', []): + if not ip_address.assigned_object or getattr(ip_address.assigned_object, 'parent_object') != parent: + raise forms.ValidationError( + _("{ip} is not assigned to this parent.").format(ip=ip_address) + ) + + return self.cleaned_data diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index 4cfe9f872..dcd9ab5e2 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -278,7 +278,7 @@ class IPRangeFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFi model = IPRange fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('family', 'vrf_id', 'status', 'role_id', 'mark_utilized', name=_('Attributes')), + FieldSet('family', 'vrf_id', 'status', 'role_id', 'mark_populated', 'mark_utilized', name=_('Attributes')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) @@ -304,6 +304,13 @@ class IPRangeFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFi null_option='None', label=_('Role') ) + mark_populated = forms.NullBooleanField( + required=False, + label=_('Treat as populated'), + widget=forms.Select( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) mark_utilized = forms.NullBooleanField( required=False, label=_('Treat as fully utilized'), @@ -425,12 +432,13 @@ class FHRPGroupFilterForm(NetBoxModelFilterSetForm): tag = TagFilterField(model) -class VLANGroupFilterForm(NetBoxModelFilterSetForm): +class VLANGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('region', 'site_group', 'site', 'location', 'rack', name=_('Location')), FieldSet('cluster_group', 'cluster', name=_('Cluster')), FieldSet('contains_vid', name=_('VLANs')), + FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), ) model = VLANGroup region = DynamicModelMultipleChoiceField( @@ -609,7 +617,7 @@ class ServiceFilterForm(ContactModelFilterForm, ServiceTemplateFilterForm): fieldsets = ( FieldSet('q', 'filter_id', 'tag'), FieldSet('protocol', 'port', name=_('Attributes')), - FieldSet('device_id', 'virtual_machine_id', name=_('Assignment')), + FieldSet('device_id', 'virtual_machine_id', 'fhrpgroup_id', name=_('Assignment')), FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) device_id = DynamicModelMultipleChoiceField( @@ -622,4 +630,9 @@ class ServiceFilterForm(ContactModelFilterForm, ServiceTemplateFilterForm): required=False, label=_('Virtual Machine'), ) + fhrpgroup_id = DynamicModelMultipleChoiceField( + queryset=FHRPGroup.objects.all(), + required=False, + label=_('FHRP Group'), + ) tag = TagFilterField(model) diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index 98a49524f..66674a749 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -21,7 +21,7 @@ from utilities.forms.rendering import FieldSet, InlineFields, ObjectAttribute, T from utilities.forms.utils import get_field_value from utilities.forms.widgets import DatePicker, HTMXSelect from utilities.templatetags.builtins.filters import bettertitle -from virtualization.models import VirtualMachine, VMInterface +from virtualization.models import VMInterface __all__ = ( 'AggregateForm', @@ -265,8 +265,8 @@ class IPRangeForm(TenancyForm, NetBoxModelForm): fieldsets = ( FieldSet( - 'vrf', 'start_address', 'end_address', 'role', 'status', 'mark_utilized', 'description', 'tags', - name=_('IP Range') + 'vrf', 'start_address', 'end_address', 'role', 'status', 'mark_populated', 'mark_utilized', 'description', + 'tags', name=_('IP Range') ), FieldSet('tenant_group', 'tenant', name=_('Tenancy')), ) @@ -274,8 +274,8 @@ class IPRangeForm(TenancyForm, NetBoxModelForm): class Meta: model = IPRange fields = [ - 'vrf', 'start_address', 'end_address', 'status', 'role', 'tenant_group', 'tenant', 'mark_utilized', - 'description', 'comments', 'tags', + 'vrf', 'start_address', 'end_address', 'status', 'role', 'tenant_group', 'tenant', 'mark_populated', + 'mark_utilized', 'description', 'comments', 'tags', ] @@ -605,7 +605,7 @@ class FHRPGroupAssignmentForm(forms.ModelForm): return group -class VLANGroupForm(NetBoxModelForm): +class VLANGroupForm(TenancyForm, NetBoxModelForm): slug = SlugField() vid_ranges = NumericRangeArrayField( label=_('VLAN IDs') @@ -628,12 +628,13 @@ class VLANGroupForm(NetBoxModelForm): FieldSet('name', 'slug', 'description', 'tags', name=_('VLAN Group')), FieldSet('vid_ranges', name=_('Child VLANs')), FieldSet('scope_type', 'scope', name=_('Scope')), + FieldSet('tenant_group', 'tenant', name=_('Tenancy')), ) class Meta: model = VLANGroup fields = [ - 'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tags', + 'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tenant_group', 'tenant', 'tags', ] def __init__(self, *args, **kwargs): @@ -757,16 +758,17 @@ class ServiceTemplateForm(NetBoxModelForm): class ServiceForm(NetBoxModelForm): - device = DynamicModelChoiceField( - label=_('Device'), - queryset=Device.objects.all(), - required=False, - selector=True + parent_object_type = ContentTypeChoiceField( + queryset=ContentType.objects.filter(SERVICE_ASSIGNMENT_MODELS), + widget=HTMXSelect(), + required=True, + label=_('Parent type') ) - virtual_machine = DynamicModelChoiceField( - label=_('Virtual machine'), - queryset=VirtualMachine.objects.all(), - required=False, + parent = DynamicModelChoiceField( + label=_('Parent'), + queryset=Device.objects.none(), # Initial queryset + required=True, + disabled=True, selector=True ) ports = NumericArrayField( @@ -790,11 +792,7 @@ class ServiceForm(NetBoxModelForm): fieldsets = ( FieldSet( - TabbedGroups( - FieldSet('device', name=_('Device')), - FieldSet('virtual_machine', name=_('Virtual Machine')), - ), - 'name', + 'parent_object_type', 'parent', 'name', InlineFields('protocol', 'ports', label=_('Port(s)')), 'ipaddresses', 'description', 'tags', name=_('Service') ), @@ -803,9 +801,38 @@ class ServiceForm(NetBoxModelForm): class Meta: model = Service fields = [ - 'device', 'virtual_machine', 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags', + 'name', 'protocol', 'ports', 'ipaddresses', 'description', 'comments', 'tags', + 'parent_object_type', ] + def __init__(self, *args, **kwargs): + initial = kwargs.get('initial', {}).copy() + + if (instance := kwargs.get('instance', None)) and instance.parent: + initial['parent'] = instance.parent + + kwargs['initial'] = initial + + super().__init__(*args, **kwargs) + + if (parent_object_type_id := get_field_value(self, 'parent_object_type')): + try: + parent_type = ContentType.objects.get(pk=parent_object_type_id) + model = parent_type.model_class() + self.fields['parent'].queryset = model.objects.all() + self.fields['parent'].widget.attrs['selector'] = model._meta.label_lower + self.fields['parent'].disabled = False + self.fields['parent'].label = _(bettertitle(model._meta.verbose_name)) + except ObjectDoesNotExist: + pass + + if self.instance and parent_object_type_id != self.instance.parent_object_type_id: + self.initial['parent'] = None + + def clean(self): + super().clean() + self.instance.parent = self.cleaned_data.get('parent') + class ServiceCreateForm(ServiceForm): service_template = DynamicModelChoiceField( @@ -816,10 +843,7 @@ class ServiceCreateForm(ServiceForm): fieldsets = ( FieldSet( - TabbedGroups( - FieldSet('device', name=_('Device')), - FieldSet('virtual_machine', name=_('Virtual Machine')), - ), + 'parent_object_type', 'parent', TabbedGroups( FieldSet('service_template', name=_('From Template')), FieldSet('name', 'protocol', 'ports', name=_('Custom')), @@ -830,8 +854,8 @@ class ServiceCreateForm(ServiceForm): class Meta(ServiceForm.Meta): fields = [ - 'device', 'virtual_machine', 'service_template', 'name', 'protocol', 'ports', 'ipaddresses', 'description', - 'comments', 'tags', + 'service_template', 'name', 'protocol', 'ports', 'ipaddresses', 'description', + 'comments', 'tags', 'parent_object_type', ] def __init__(self, *args, **kwargs): diff --git a/netbox/ipam/graphql/enums.py b/netbox/ipam/graphql/enums.py new file mode 100644 index 000000000..34180bfbc --- /dev/null +++ b/netbox/ipam/graphql/enums.py @@ -0,0 +1,27 @@ +import strawberry + +from ipam.choices import * + +__all__ = ( + 'FHRPGroupAuthTypeEnum', + 'FHRPGroupProtocolEnum', + 'IPAddressFamilyEnum', + 'IPAddressRoleEnum', + 'IPAddressStatusEnum', + 'IPRangeStatusEnum', + 'PrefixStatusEnum', + 'ServiceProtocolEnum', + 'VLANStatusEnum', + 'VLANQinQRoleEnum', +) + +FHRPGroupAuthTypeEnum = strawberry.enum(FHRPGroupAuthTypeChoices.as_enum(prefix='authentication')) +FHRPGroupProtocolEnum = strawberry.enum(FHRPGroupProtocolChoices.as_enum(prefix='protocol')) +IPAddressFamilyEnum = strawberry.enum(IPAddressFamilyChoices.as_enum(prefix='family')) +IPAddressRoleEnum = strawberry.enum(IPAddressRoleChoices.as_enum(prefix='role')) +IPAddressStatusEnum = strawberry.enum(IPAddressStatusChoices.as_enum(prefix='status')) +IPRangeStatusEnum = strawberry.enum(IPRangeStatusChoices.as_enum(prefix='status')) +PrefixStatusEnum = strawberry.enum(PrefixStatusChoices.as_enum(prefix='status')) +ServiceProtocolEnum = strawberry.enum(ServiceProtocolChoices.as_enum(prefix='role')) +VLANStatusEnum = strawberry.enum(VLANStatusChoices.as_enum(prefix='status')) +VLANQinQRoleEnum = strawberry.enum(VLANQinQRoleChoices.as_enum(prefix='role')) diff --git a/netbox/ipam/graphql/filter_mixins.py b/netbox/ipam/graphql/filter_mixins.py new file mode 100644 index 000000000..511850285 --- /dev/null +++ b/netbox/ipam/graphql/filter_mixins.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django + +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerLookup + from .enums import * + +__all__ = ( + 'ServiceBaseFilterMixin', +) + + +@dataclass +class ServiceBaseFilterMixin(BaseFilterMixin): + protocol: Annotated['ServiceProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + ports: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 1b0e0133b..4e1afc3ba 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -1,7 +1,28 @@ -import strawberry_django +from datetime import date +from typing import Annotated, TYPE_CHECKING -from ipam import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +import netaddr +import strawberry +import strawberry_django +from django.db.models import Q +from netaddr.core import AddrFormatError +from strawberry.scalars import ID +from strawberry_django import FilterLookup, DateFilterLookup + +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from dcim.graphql.filter_mixins import ScopedFilterMixin +from ipam import models +from ipam.graphql.filter_mixins import ServiceBaseFilterMixin +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, OrganizationalModelFilterMixin, PrimaryModelFilterMixin +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin + +if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerArrayLookup, IntegerLookup + from circuits.graphql.filters import ProviderFilter + from core.graphql.filters import ContentTypeFilter + from dcim.graphql.filters import SiteFilter + from vpn.graphql.filters import L2VPNFilter + from .enums import * __all__ = ( 'ASNFilter', @@ -26,108 +47,273 @@ __all__ = ( @strawberry_django.filter(models.ASN, lookups=True) -@autotype_decorator(filtersets.ASNFilterSet) -class ASNFilter(BaseFilterMixin): - pass +class ASNFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + rir_id: ID | None = strawberry_django.filter_field() + asn: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + sites: ( + Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None + ) = strawberry_django.filter_field() + providers: ( + Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None + ) = strawberry_django.filter_field() @strawberry_django.filter(models.ASNRange, lookups=True) -@autotype_decorator(filtersets.ASNRangeFilterSet) -class ASNRangeFilter(BaseFilterMixin): - pass +class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + rir_id: ID | None = strawberry_django.filter_field() + start: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + end: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Aggregate, lookups=True) -@autotype_decorator(filtersets.AggregateFilterSet) -class AggregateFilter(BaseFilterMixin): - pass +class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + prefix: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + prefix_id: ID | None = strawberry_django.filter_field() + rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + rir_id: ID | None = strawberry_django.filter_field() + date_added: DateFilterLookup[date] | None = strawberry_django.filter_field() @strawberry_django.filter(models.FHRPGroup, lookups=True) -@autotype_decorator(filtersets.FHRPGroupFilterSet) -class FHRPGroupFilter(BaseFilterMixin): - pass +class FHRPGroupFilter(PrimaryModelFilterMixin): + group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + protocol: Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_type: Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_key: FilterLookup[str] | None = strawberry_django.filter_field() + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.FHRPGroupAssignment, lookups=True) -@autotype_decorator(filtersets.FHRPGroupAssignmentFilterSet) -class FHRPGroupAssignmentFilter(BaseFilterMixin): - pass +class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + interface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_id: FilterLookup[str] | None = strawberry_django.filter_field() + group: Annotated['FHRPGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + priority: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IPAddress, lookups=True) -@autotype_decorator(filtersets.IPAddressFilterSet) -class IPAddressFilter(BaseFilterMixin): - pass +class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + address: FilterLookup[str] | None = strawberry_django.filter_field() + vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + vrf_id: ID | None = strawberry_django.filter_field() + status: Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_id: ID | None = strawberry_django.filter_field() + nat_inside: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + nat_inside_id: ID | None = strawberry_django.filter_field() + nat_outside: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + nat_outside_id: ID | None = strawberry_django.filter_field() + dns_name: FilterLookup[str] | None = strawberry_django.filter_field() + + @strawberry_django.filter_field() + def parent(self, value: list[str], prefix) -> Q: + if not value: + return Q() + q = Q() + for subnet in value: + try: + query = str(netaddr.IPNetwork(subnet.strip()).cidr) + q |= Q(address__net_host_contained=query) + except (AddrFormatError, ValueError): + return Q() + return q @strawberry_django.filter(models.IPRange, lookups=True) -@autotype_decorator(filtersets.IPRangeFilterSet) -class IPRangeFilter(BaseFilterMixin): - pass +class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + start_address: FilterLookup[str] | None = strawberry_django.filter_field() + end_address: FilterLookup[str] | None = strawberry_django.filter_field() + size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + vrf_id: ID | None = strawberry_django.filter_field() + status: Annotated['IPRangeStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field() + + @strawberry_django.filter_field() + def parent(self, value: list[str], prefix) -> Q: + if not value: + return Q() + q = Q() + for subnet in value: + try: + query = str(netaddr.IPNetwork(subnet.strip()).cidr) + q |= Q(start_address__net_host_contained=query, end_address__net_host_contained=query) + except (AddrFormatError, ValueError): + return Q() + return q @strawberry_django.filter(models.Prefix, lookups=True) -@autotype_decorator(filtersets.PrefixFilterSet) -class PrefixFilter(BaseFilterMixin): - pass +class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + prefix: FilterLookup[str] | None = strawberry_django.filter_field() + vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + vrf_id: ID | None = strawberry_django.filter_field() + vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + vlan_id: ID | None = strawberry_django.filter_field() + status: Annotated['PrefixStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['RoleFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + role_id: ID | None = strawberry_django.filter_field() + is_pool: FilterLookup[bool] | None = strawberry_django.filter_field() + mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.RIR, lookups=True) -@autotype_decorator(filtersets.RIRFilterSet) -class RIRFilter(BaseFilterMixin): - pass +class RIRFilter(OrganizationalModelFilterMixin): + is_private: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Role, lookups=True) -@autotype_decorator(filtersets.RoleFilterSet) -class RoleFilter(BaseFilterMixin): - pass +class RoleFilter(OrganizationalModelFilterMixin): + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RouteTarget, lookups=True) -@autotype_decorator(filtersets.RouteTargetFilterSet) -class RouteTargetFilter(BaseFilterMixin): - pass +class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + importing_vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + exporting_vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + importing_l2vpns: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + exporting_l2vpns: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Service, lookups=True) -@autotype_decorator(filtersets.ServiceFilterSet) -class ServiceFilter(BaseFilterMixin): - pass +class ServiceFilter(ContactFilterMixin, ServiceBaseFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_object_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.ServiceTemplate, lookups=True) -@autotype_decorator(filtersets.ServiceTemplateFilterSet) -class ServiceTemplateFilter(BaseFilterMixin): - pass +class ServiceTemplateFilter(ServiceBaseFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VLAN, lookups=True) -@autotype_decorator(filtersets.VLANFilterSet) -class VLANFilter(BaseFilterMixin): - pass +class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + group: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = strawberry_django.filter_field() + role: Annotated['RoleFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + role_id: ID | None = strawberry_django.filter_field() + qinq_svlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + qinq_svlan_id: ID | None = strawberry_django.filter_field() + qinq_cvlans: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + qinq_role: Annotated['VLANQinQRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VLANGroup, lookups=True) -@autotype_decorator(filtersets.VLANGroupFilterSet) -class VLANGroupFilter(BaseFilterMixin): - pass +class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilterMixin): + vid_ranges: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VLANTranslationPolicy, lookups=True) -@autotype_decorator(filtersets.VLANTranslationPolicyFilterSet) -class VLANTranslationPolicyFilter(BaseFilterMixin): - pass +class VLANTranslationPolicyFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VLANTranslationRule, lookups=True) -@autotype_decorator(filtersets.VLANTranslationRuleFilterSet) -class VLANTranslationRuleFilter(BaseFilterMixin): - pass +class VLANTranslationRuleFilter(NetBoxModelFilterMixin): + policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + policy_id: ID | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + local_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + remote_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VRF, lookups=True) -@autotype_decorator(filtersets.VRFFilterSet) -class VRFFilter(BaseFilterMixin): - pass +class VRFFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + rd: FilterLookup[str] | None = strawberry_django.filter_field() + enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field() + import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + export_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index 54ce2fc74..e8f98eabe 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -12,6 +12,21 @@ from netbox.graphql.types import BaseObjectType, NetBoxObjectType, Organizationa from .filters import * from .mixins import IPAddressesMixin +if TYPE_CHECKING: + from dcim.graphql.types import ( + DeviceType, + InterfaceType, + LocationType, + RackType, + RegionType, + SiteGroupType, + SiteType, + ) + from tenancy.graphql.types import TenantType + from virtualization.graphql.types import ClusterGroupType, ClusterType, VMInterfaceType, VirtualMachineType + from vpn.graphql.types import L2VPNType, TunnelTerminationType + from wireless.graphql.types import WirelessLANType + __all__ = ( 'ASNType', 'ASNRangeType', @@ -56,7 +71,8 @@ class BaseIPAddressFamilyType: @strawberry_django.type( models.ASN, fields='__all__', - filters=ASNFilter + filters=ASNFilter, + pagination=True ) class ASNType(NetBoxObjectType): asn: BigInt @@ -70,7 +86,8 @@ class ASNType(NetBoxObjectType): @strawberry_django.type( models.ASNRange, fields='__all__', - filters=ASNRangeFilter + filters=ASNRangeFilter, + pagination=True ) class ASNRangeType(NetBoxObjectType): start: BigInt @@ -82,7 +99,8 @@ class ASNRangeType(NetBoxObjectType): @strawberry_django.type( models.Aggregate, fields='__all__', - filters=AggregateFilter + filters=AggregateFilter, + pagination=True ) class AggregateType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): prefix: str @@ -93,7 +111,8 @@ class AggregateType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): @strawberry_django.type( models.FHRPGroup, fields='__all__', - filters=FHRPGroupFilter + filters=FHRPGroupFilter, + pagination=True ) class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): @@ -102,8 +121,9 @@ class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): @strawberry_django.type( models.FHRPGroupAssignment, - exclude=('interface_type', 'interface_id'), - filters=FHRPGroupAssignmentFilter + exclude=['interface_type', 'interface_id'], + filters=FHRPGroupAssignmentFilter, + pagination=True ) class FHRPGroupAssignmentType(BaseObjectType): group: Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')] @@ -118,8 +138,9 @@ class FHRPGroupAssignmentType(BaseObjectType): @strawberry_django.type( models.IPAddress, - exclude=('assigned_object_type', 'assigned_object_id', 'address'), - filters=IPAddressFilter + exclude=['assigned_object_type', 'assigned_object_id', 'address'], + filters=IPAddressFilter, + pagination=True ) class IPAddressType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): address: str @@ -143,7 +164,8 @@ class IPAddressType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): @strawberry_django.type( models.IPRange, fields='__all__', - filters=IPRangeFilter + filters=IPRangeFilter, + pagination=True ) class IPRangeType(NetBoxObjectType, ContactsMixin): start_address: str @@ -155,8 +177,9 @@ class IPRangeType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.Prefix, - exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), - filters=PrefixFilter + exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], + filters=PrefixFilter, + pagination=True ) class PrefixType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): prefix: str @@ -178,7 +201,8 @@ class PrefixType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): @strawberry_django.type( models.RIR, fields='__all__', - filters=RIRFilter + filters=RIRFilter, + pagination=True ) class RIRType(OrganizationalObjectType): @@ -190,7 +214,8 @@ class RIRType(OrganizationalObjectType): @strawberry_django.type( models.Role, fields='__all__', - filters=RoleFilter + filters=RoleFilter, + pagination=True ) class RoleType(OrganizationalObjectType): @@ -202,7 +227,8 @@ class RoleType(OrganizationalObjectType): @strawberry_django.type( models.RouteTarget, fields='__all__', - filters=RouteTargetFilter + filters=RouteTargetFilter, + pagination=True ) class RouteTargetType(NetBoxObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -215,21 +241,28 @@ class RouteTargetType(NetBoxObjectType): @strawberry_django.type( models.Service, - fields='__all__', - filters=ServiceFilter + exclude=('parent_object_type', 'parent_object_id'), + filters=ServiceFilter, + pagination=True ) class ServiceType(NetBoxObjectType, ContactsMixin): ports: List[int] - device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None - virtual_machine: Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')] | None - ipaddresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]] + @strawberry_django.field + def parent(self) -> Annotated[Union[ + Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')], + Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')], + Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')], + ], strawberry.union("ServiceParentType")] | None: + return self.parent + @strawberry_django.type( models.ServiceTemplate, fields='__all__', - filters=ServiceTemplateFilter + filters=ServiceTemplateFilter, + pagination=True ) class ServiceTemplateType(NetBoxObjectType): ports: List[int] @@ -237,8 +270,9 @@ class ServiceTemplateType(NetBoxObjectType): @strawberry_django.type( models.VLAN, - exclude=('qinq_svlan',), - filters=VLANFilter + exclude=['qinq_svlan'], + filters=VLANFilter, + pagination=True ) class VLANType(NetBoxObjectType): site: Annotated["SiteType", strawberry.lazy('ipam.graphql.types')] | None @@ -260,13 +294,15 @@ class VLANType(NetBoxObjectType): @strawberry_django.type( models.VLANGroup, - exclude=('scope_type', 'scope_id'), - filters=VLANGroupFilter + exclude=['scope_type', 'scope_id'], + filters=VLANGroupFilter, + pagination=True ) class VLANGroupType(OrganizationalObjectType): vlans: List[VLANType] vid_ranges: List[str] + tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @strawberry_django.field def scope(self) -> Annotated[Union[ @@ -284,7 +320,8 @@ class VLANGroupType(OrganizationalObjectType): @strawberry_django.type( models.VLANTranslationPolicy, fields='__all__', - filters=VLANTranslationPolicyFilter + filters=VLANTranslationPolicyFilter, + pagination=True ) class VLANTranslationPolicyType(NetBoxObjectType): rules: List[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]] @@ -293,7 +330,8 @@ class VLANTranslationPolicyType(NetBoxObjectType): @strawberry_django.type( models.VLANTranslationRule, fields='__all__', - filters=VLANTranslationRuleFilter + filters=VLANTranslationRuleFilter, + pagination=True ) class VLANTranslationRuleType(NetBoxObjectType): policy: Annotated[ @@ -305,7 +343,8 @@ class VLANTranslationRuleType(NetBoxObjectType): @strawberry_django.type( models.VRF, fields='__all__', - filters=VRFFilter + filters=VRFFilter, + pagination=True ) class VRFType(NetBoxObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None diff --git a/netbox/ipam/migrations/0001_squashed.py b/netbox/ipam/migrations/0001_squashed.py index 896d7c4c9..15fb71dde 100644 --- a/netbox/ipam/migrations/0001_squashed.py +++ b/netbox/ipam/migrations/0001_squashed.py @@ -13,9 +13,9 @@ class Migration(migrations.Migration): dependencies = [ ('contenttypes', '0002_remove_content_type_name'), - ('dcim', '0002_auto_20160622_1821'), - ('extras', '0001_initial'), - ('tenancy', '0001_initial'), + ('dcim', '0002_squashed'), + ('extras', '0001_squashed'), + ('tenancy', '0001_squashed_0012'), ] replaces = [ @@ -195,12 +195,6 @@ class Migration(migrations.Migration): 'scope_type', models.ForeignKey( blank=True, - limit_choices_to=models.Q( - ( - 'model__in', - ('region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster'), - ) - ), null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', diff --git a/netbox/ipam/migrations/0002_squashed_0046.py b/netbox/ipam/migrations/0002_squashed_0046.py index 6c03753d8..43b1223d0 100644 --- a/netbox/ipam/migrations/0002_squashed_0046.py +++ b/netbox/ipam/migrations/0002_squashed_0046.py @@ -5,12 +5,12 @@ import taggit.managers class Migration(migrations.Migration): dependencies = [ - ('dcim', '0003_auto_20160628_1721'), - ('virtualization', '0001_virtualization'), + ('dcim', '0003_squashed_0130'), + ('virtualization', '0001_squashed_0022'), ('contenttypes', '0002_remove_content_type_name'), - ('ipam', '0001_initial'), - ('extras', '0002_custom_fields'), - ('tenancy', '0001_initial'), + ('ipam', '0001_squashed'), + ('extras', '0002_squashed_0059'), + ('tenancy', '0001_squashed_0012'), ] replaces = [ @@ -154,13 +154,6 @@ class Migration(migrations.Migration): name='assigned_object_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'dcim'), ('model', 'interface')), - models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), - _connector='OR', - ) - ), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/ipam/migrations/0047_squashed_0053.py b/netbox/ipam/migrations/0047_squashed_0053.py index a05d0cb81..151792eb6 100644 --- a/netbox/ipam/migrations/0047_squashed_0053.py +++ b/netbox/ipam/migrations/0047_squashed_0053.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): ] dependencies = [ - ('ipam', '0046_set_vlangroup_scope_types'), + ('ipam', '0002_squashed_0046'), ('tenancy', '0001_squashed_0012'), ('extras', '0002_squashed_0059'), ('contenttypes', '0002_remove_content_type_name'), @@ -136,14 +136,6 @@ class Migration(migrations.Migration): name='assigned_object_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'dcim'), ('model', 'interface')), - models.Q(('app_label', 'ipam'), ('model', 'fhrpgroup')), - models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), - _connector='OR', - ) - ), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/ipam/migrations/0054_squashed_0067.py b/netbox/ipam/migrations/0054_squashed_0067.py index 929a27fda..26bd54115 100644 --- a/netbox/ipam/migrations/0054_squashed_0067.py +++ b/netbox/ipam/migrations/0054_squashed_0067.py @@ -28,11 +28,11 @@ class Migration(migrations.Migration): ] dependencies = [ - ('tenancy', '0007_contact_link'), + ('tenancy', '0002_squashed_0011'), ('contenttypes', '0002_remove_content_type_name'), ('extras', '0060_squashed_0086'), - ('ipam', '0053_asn_model'), - ('tenancy', '0009_standardize_description_comments'), + ('ipam', '0047_squashed_0053'), + ('tenancy', '0002_squashed_0011'), ] operations = [ @@ -304,14 +304,6 @@ class Migration(migrations.Migration): ( 'assigned_object_type', models.ForeignKey( - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'dcim'), ('model', 'interface')), - models.Q(('app_label', 'ipam'), ('model', 'vlan')), - models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), - _connector='OR', - ) - ), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype', diff --git a/netbox/ipam/migrations/0068_move_l2vpn.py b/netbox/ipam/migrations/0068_move_l2vpn.py index 9240240bc..16935b1a6 100644 --- a/netbox/ipam/migrations/0068_move_l2vpn.py +++ b/netbox/ipam/migrations/0068_move_l2vpn.py @@ -16,7 +16,7 @@ def update_content_types(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('ipam', '0067_ipaddress_index_host'), + ('ipam', '0054_squashed_0067'), ] operations = [ diff --git a/netbox/ipam/migrations/0071_prefix_scope.py b/netbox/ipam/migrations/0071_prefix_scope.py index 2ab54d023..47a971750 100644 --- a/netbox/ipam/migrations/0071_prefix_scope.py +++ b/netbox/ipam/migrations/0071_prefix_scope.py @@ -33,7 +33,6 @@ class Migration(migrations.Migration): name='scope_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q(('model__in', ('region', 'sitegroup', 'site', 'location'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/ipam/migrations/0077_vlangroup_tenant.py b/netbox/ipam/migrations/0077_vlangroup_tenant.py new file mode 100644 index 000000000..9fb67cf53 --- /dev/null +++ b/netbox/ipam/migrations/0077_vlangroup_tenant.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.3 on 2025-02-20 17:49 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0076_natural_ordering'), + ('tenancy', '0017_natural_ordering'), + ] + + operations = [ + migrations.AddField( + model_name='vlangroup', + name='tenant', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='vlan_groups', + to='tenancy.tenant', + ), + ), + ] diff --git a/netbox/ipam/migrations/0078_iprange_mark_utilized.py b/netbox/ipam/migrations/0078_iprange_mark_utilized.py new file mode 100644 index 000000000..95da5387b --- /dev/null +++ b/netbox/ipam/migrations/0078_iprange_mark_utilized.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0077_vlangroup_tenant'), + ] + + operations = [ + migrations.AddField( + model_name='iprange', + name='mark_populated', + field=models.BooleanField(default=False), + ), + ] diff --git a/netbox/ipam/migrations/0079_add_service_fhrp_group_parent_gfk.py b/netbox/ipam/migrations/0079_add_service_fhrp_group_parent_gfk.py new file mode 100644 index 000000000..4ae5fd271 --- /dev/null +++ b/netbox/ipam/migrations/0079_add_service_fhrp_group_parent_gfk.py @@ -0,0 +1,29 @@ +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('ipam', '0078_iprange_mark_utilized'), + ] + + operations = [ + migrations.AddField( + model_name='service', + name='parent_object_id', + field=models.PositiveBigIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='service', + name='parent_object_type', + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name='+', + to='contenttypes.contenttype' + ), + ), + ] diff --git a/netbox/ipam/migrations/0080_populate_service_parent.py b/netbox/ipam/migrations/0080_populate_service_parent.py new file mode 100644 index 000000000..78f3086fc --- /dev/null +++ b/netbox/ipam/migrations/0080_populate_service_parent.py @@ -0,0 +1,54 @@ +from django.db import migrations +from django.db.models import F + + +def populate_service_parent_gfk(apps, schema_config): + Service = apps.get_model('ipam', 'Service') + ContentType = apps.get_model('contenttypes', 'ContentType') + Device = apps.get_model('dcim', 'device') + VirtualMachine = apps.get_model('virtualization', 'virtualmachine') + + Service.objects.filter(device_id__isnull=False).update( + parent_object_type=ContentType.objects.get_for_model(Device), + parent_object_id=F('device_id'), + ) + + Service.objects.filter(virtual_machine_id__isnull=False).update( + parent_object_type=ContentType.objects.get_for_model(VirtualMachine), + parent_object_id=F('virtual_machine_id'), + ) + + +def repopulate_device_and_virtualmachine_relations(apps, schemaconfig): + Service = apps.get_model('ipam', 'Service') + ContentType = apps.get_model('contenttypes', 'ContentType') + Device = apps.get_model('dcim', 'device') + VirtualMachine = apps.get_model('virtualization', 'virtualmachine') + + Service.objects.filter( + parent_object_type=ContentType.objects.get_for_model(Device), + ).update( + device_id=F('parent_object_id') + ) + + Service.objects.filter( + parent_object_type=ContentType.objects.get_for_model(VirtualMachine), + ).update( + virtual_machine_id=F('parent_object_id') + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0202_location_comments_region_comments_sitegroup_comments'), + ('ipam', '0079_add_service_fhrp_group_parent_gfk'), + ('virtualization', '0048_populate_mac_addresses'), + ] + + operations = [ + migrations.RunPython( + populate_service_parent_gfk, + reverse_code=repopulate_device_and_virtualmachine_relations, + ) + ] diff --git a/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py b/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py new file mode 100644 index 000000000..03b63cd12 --- /dev/null +++ b/netbox/ipam/migrations/0081_remove_service_device_virtual_machine_add_parent_gfk_index.py @@ -0,0 +1,39 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('extras', '0126_exporttemplate_file_name'), + ('ipam', '0080_populate_service_parent'), + ] + + operations = [ + migrations.RemoveField( + model_name='service', + name='device', + ), + migrations.RemoveField( + model_name='service', + name='virtual_machine', + ), + migrations.AlterField( + model_name='service', + name='parent_object_id', + field=models.PositiveBigIntegerField(), + ), + migrations.AlterField( + model_name='service', + name='parent_object_type', + field=models.ForeignKey( + on_delete=models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype' + ), + ), + migrations.AddIndex( + model_name='service', + index=models.Index( + fields=['parent_object_type', 'parent_object_id'], name='ipam_servic_parent__563d2b_idx' + ), + ), + ] diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index f5982853e..63a04d4d1 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -48,6 +48,12 @@ class FHRPGroup(PrimaryModel): object_id_field='assigned_object_id', related_query_name='fhrpgroup' ) + services = GenericRelation( + to='ipam.Service', + content_type_field='parent_object_type', + object_id_field='parent_object_id', + related_query_name='fhrpgroup', + ) clone_fields = ('protocol', 'auth_type', 'auth_key', 'description') diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index e1a8d91e3..ab2481d90 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -383,14 +383,15 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary else: return Prefix.objects.filter(prefix__net_contained=str(self.prefix), vrf=self.vrf) - def get_child_ranges(self): + def get_child_ranges(self, **kwargs): """ Return all IPRanges within this Prefix and VRF. """ return IPRange.objects.filter( vrf=self.vrf, start_address__net_host_contained=str(self.prefix), - end_address__net_host_contained=str(self.prefix) + end_address__net_host_contained=str(self.prefix), + **kwargs ) def get_child_ips(self): @@ -407,15 +408,14 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary """ Return all available IPs within this prefix as an IPSet. """ - if self.mark_utilized: - return netaddr.IPSet() - prefix = netaddr.IPSet(self.prefix) - child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]) - child_ranges = [] - for iprange in self.get_child_ranges(): - child_ranges.append(iprange.range) - available_ips = prefix - child_ips - netaddr.IPSet(child_ranges) + child_ips = netaddr.IPSet([ + ip.address.ip for ip in self.get_child_ips() + ]) + child_ranges = netaddr.IPSet([ + iprange.range for iprange in self.get_child_ranges().filter(mark_populated=True) + ]) + available_ips = prefix - child_ips - child_ranges # IPv6 /127's, pool, or IPv4 /31-/32 sets are fully usable if (self.family == 6 and self.prefix.prefixlen >= 127) or self.is_pool or ( @@ -433,6 +433,7 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary # For IPv6 prefixes, omit the Subnet-Router anycast address # per RFC 4291 available_ips -= netaddr.IPSet([netaddr.IPAddress(self.prefix.first)]) + return available_ips def get_first_available_ip(self): @@ -461,9 +462,11 @@ class Prefix(ContactsMixin, GetAvailablePrefixesMixin, CachedScopeMixin, Primary utilization = float(child_prefixes.size) / self.prefix.size * 100 else: # Compile an IPSet to avoid counting duplicate IPs - child_ips = netaddr.IPSet( - [_.range for _ in self.get_child_ranges()] + [_.address.ip for _ in self.get_child_ips()] - ) + child_ips = netaddr.IPSet() + for iprange in self.get_child_ranges().filter(mark_utilized=True): + child_ips.add(iprange.range) + for ip in self.get_child_ips(): + child_ips.add(ip.address.ip) prefix_size = self.prefix.size if self.prefix.version == 4 and self.prefix.prefixlen < 31 and not self.is_pool: @@ -519,14 +522,19 @@ class IPRange(ContactsMixin, PrimaryModel): null=True, help_text=_('The primary function of this range') ) + mark_populated = models.BooleanField( + verbose_name=_('mark populated'), + default=False, + help_text=_("Prevent the creation of IP addresses within this range") + ) mark_utilized = models.BooleanField( verbose_name=_('mark utilized'), default=False, - help_text=_("Treat as fully utilized") + help_text=_("Report space as 100% utilized") ) clone_fields = ( - 'vrf', 'tenant', 'status', 'role', 'description', + 'vrf', 'tenant', 'status', 'role', 'description', 'mark_populated', 'mark_utilized', ) class Meta: @@ -663,6 +671,9 @@ class IPRange(ContactsMixin, PrimaryModel): """ Return all available IPs within this range as an IPSet. """ + if self.mark_populated: + return netaddr.IPSet() + range = netaddr.IPRange(self.start_address.ip, self.end_address.ip) child_ips = netaddr.IPSet([ip.address.ip for ip in self.get_child_ips()]) @@ -742,7 +753,6 @@ class IPAddress(ContactsMixin, PrimaryModel): ) assigned_object_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=IPADDRESS_ASSIGNMENT_MODELS, on_delete=models.PROTECT, related_name='+', blank=True, @@ -876,6 +886,20 @@ class IPAddress(ContactsMixin, PrimaryModel): ) }) + # Disallow the creation of IPAddresses within an IPRange with mark_populated=True + parent_range = IPRange.objects.filter( + start_address__lte=self.address, + end_address__gte=self.address, + vrf=self.vrf, + mark_populated=True + ).first() + if parent_range: + raise ValidationError({ + 'address': _( + "Cannot create IP address {ip} inside range {range}." + ).format(ip=self.address, range=parent_range) + }) + if self._original_assigned_object_id and self._original_assigned_object_type_id: parent = getattr(self.assigned_object, 'parent_object', None) ct = ObjectType.objects.get_for_id(self._original_assigned_object_type_id) diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index bb4049781..2afd16076 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -1,5 +1,5 @@ +from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.postgres.fields import ArrayField -from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.utils.translation import gettext_lazy as _ @@ -64,21 +64,17 @@ class Service(ContactsMixin, ServiceBase, PrimaryModel): A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may optionally be tied to one or more specific IPAddresses belonging to its parent. """ - device = models.ForeignKey( - to='dcim.Device', - on_delete=models.CASCADE, - related_name='services', - verbose_name=_('device'), - null=True, - blank=True + parent_object_type = models.ForeignKey( + to='contenttypes.ContentType', + on_delete=models.PROTECT, + related_name='+', ) - virtual_machine = models.ForeignKey( - to='virtualization.VirtualMachine', - on_delete=models.CASCADE, - related_name='services', - null=True, - blank=True + parent_object_id = models.PositiveBigIntegerField() + parent = GenericForeignKey( + ct_field='parent_object_type', + fk_field='parent_object_id' ) + name = models.CharField( max_length=100, verbose_name=_('name') @@ -91,22 +87,12 @@ class Service(ContactsMixin, ServiceBase, PrimaryModel): help_text=_("The specific IP addresses (if any) to which this service is bound") ) - clone_fields = ['protocol', 'ports', 'description', 'device', 'virtual_machine', 'ipaddresses', ] + clone_fields = ['protocol', 'ports', 'description', 'parent', 'ipaddresses', ] class Meta: + indexes = ( + models.Index(fields=('parent_object_type', 'parent_object_id')), + ) ordering = ('protocol', 'ports', 'pk') # (protocol, port) may be non-unique verbose_name = _('service') verbose_name_plural = _('services') - - @property - def parent(self): - return self.device or self.virtual_machine - - def clean(self): - super().clean() - - # A Service must belong to a Device *or* to a VirtualMachine - if self.device and self.virtual_machine: - raise ValidationError(_("A service cannot be associated with both a device and a virtual machine.")) - if not self.device and not self.virtual_machine: - raise ValidationError(_("A service must be associated with either a device or a virtual machine.")) diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index 0288daf2e..67c9d9414 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -46,7 +46,6 @@ class VLANGroup(OrganizationalModel): scope_type = models.ForeignKey( to='contenttypes.ContentType', on_delete=models.CASCADE, - limit_choices_to=Q(model__in=VLANGROUP_SCOPE_TYPES), blank=True, null=True ) @@ -63,6 +62,13 @@ class VLANGroup(OrganizationalModel): verbose_name=_('VLAN ID ranges'), default=default_vid_ranges ) + tenant = models.ForeignKey( + to='tenancy.Tenant', + on_delete=models.PROTECT, + related_name='vlan_groups', + blank=True, + null=True + ) _total_vlan_ids = models.PositiveBigIntegerField( default=VLAN_VID_MAX - VLAN_VID_MIN + 1 ) diff --git a/netbox/ipam/search.py b/netbox/ipam/search.py index 6e71d44a5..63437e417 100644 --- a/netbox/ipam/search.py +++ b/netbox/ipam/search.py @@ -123,7 +123,7 @@ class ServiceIndex(SearchIndex): ('description', 500), ('comments', 5000), ) - display_attrs = ('device', 'virtual_machine', 'description') + display_attrs = ('parent', 'description') @register_search diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index 1eefa6b3a..03365a442 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -10,6 +10,7 @@ from .template_code import * __all__ = ( 'AggregateTable', + 'AnnotatedIPAddressTable', 'AssignedIPAddressesTable', 'IPAddressAssignTable', 'IPAddressTable', @@ -268,6 +269,10 @@ class IPRangeTable(TenancyColumnsMixin, NetBoxTable): verbose_name=_('Role'), linkify=True ) + mark_populated = columns.BooleanColumn( + verbose_name=_('Marked Populated'), + false_mark=None + ) mark_utilized = columns.BooleanColumn( verbose_name=_('Marked Utilized'), false_mark=None @@ -288,7 +293,8 @@ class IPRangeTable(TenancyColumnsMixin, NetBoxTable): model = IPRange fields = ( 'pk', 'id', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'tenant_group', - 'mark_utilized', 'utilization', 'description', 'comments', 'tags', 'created', 'last_updated', + 'mark_populated', 'mark_utilized', 'utilization', 'description', 'comments', 'tags', 'created', + 'last_updated', ) default_columns = ( 'pk', 'start_address', 'end_address', 'size', 'vrf', 'status', 'role', 'tenant', 'description', @@ -369,6 +375,16 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable): } +class AnnotatedIPAddressTable(IPAddressTable): + address = tables.TemplateColumn( + template_code=IPADDRESS_LINK, + verbose_name=_('IP Address') + ) + + class Meta(IPAddressTable.Meta): + pass + + class IPAddressAssignTable(NetBoxTable): address = tables.TemplateColumn( template_code=IPADDRESS_ASSIGN_LINK, diff --git a/netbox/ipam/tables/template_code.py b/netbox/ipam/tables/template_code.py index fb969345e..14b73b28d 100644 --- a/netbox/ipam/tables/template_code.py +++ b/netbox/ipam/tables/template_code.py @@ -25,18 +25,23 @@ PREFIX_LINK_WITH_DEPTH = """ {% endif %} """ + PREFIX_LINK +# Annotate the ID of each IP address for copy-to-clipboard functionality IPADDRESS_LINK = """ -{% if record.pk %} - {{ record.address }} +{% if record.address %} + {{ record }} +{% elif record.start_address %} + {{ record }} {% elif perms.ipam.add_ipaddress %} - {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available + {{ record.title }} {% else %} - {% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available + {{ record.title }} {% endif %} """ IPADDRESS_COPY_BUTTON = """ -{% copy_content record.pk prefix="ipaddress_" %} +{% if record.address %} + {% copy_content record.pk prefix="ipaddress_" %} +{% endif %} """ IPADDRESS_ASSIGN_LINK = """ diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index aa1900e41..c22975be0 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -28,7 +28,7 @@ AVAILABLE_LABEL = mark_safe('Available first_ip_in_prefix: - skipped_count = int(ipaddress_list[0].address.ip - first_ip_in_prefix) - first_skipped = '{}/{}'.format(first_ip_in_prefix, prefix.prefixlen) - output.append((skipped_count, first_skipped)) + if records[0][0] > first_ip_in_prefix: + output.append(AvailableIPSpace( + size=int(records[0][0] - first_ip_in_prefix), + first_ip=f'{first_ip_in_prefix}/{prefix.mask_length}' + )) - # Iterate through existing IPs and annotate free ranges - for ip in ipaddress_list: + # Add IP ranges & addresses, annotating available space in between records + for record in records: if prev_ip: - diff = int(ip.address.ip - prev_ip.address.ip) - if diff > 1: - first_skipped = '{}/{}'.format(prev_ip.address.ip + 1, prefix.prefixlen) - output.append((diff - 1, first_skipped)) - output.append(ip) - prev_ip = ip + # Annotate available space + if (diff := int(record[0]) - int(prev_ip)) > 1: + first_skipped = f'{prev_ip + 1}/{prefix.mask_length}' + output.append(AvailableIPSpace( + size=diff - 1, + first_ip=first_skipped + )) + + output.append(record[1]) + + # Update the previous IP address + if hasattr(record[1], 'end_address'): + prev_ip = record[1].end_address.ip + else: + prev_ip = record[0] # Include any remaining available IPs - if prev_ip.address.ip < last_ip_in_prefix: - skipped_count = int(last_ip_in_prefix - prev_ip.address.ip) - first_skipped = '{}/{}'.format(prev_ip.address.ip + 1, prefix.prefixlen) - output.append((skipped_count, first_skipped)) + if prev_ip < last_ip_in_prefix: + output.append(AvailableIPSpace( + size=int(last_ip_in_prefix - prev_ip), + first_ip=f'{prev_ip + 1}/{prefix.mask_length}' + )) return output diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index e1c6f170c..34ce6144f 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -8,21 +8,20 @@ from django.utils.translation import gettext_lazy as _ from circuits.models import Provider from dcim.filtersets import InterfaceFilterSet from dcim.forms import InterfaceFilterForm -from dcim.models import Interface, Site +from dcim.models import Device, Interface, Site from ipam.tables import VLANTranslationRuleTable from netbox.views import generic -from tenancy.views import ObjectContactsView from utilities.query import count_related from utilities.tables import get_table_ordering from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view from virtualization.filtersets import VMInterfaceFilterSet from virtualization.forms import VMInterfaceFilterForm -from virtualization.models import VMInterface +from virtualization.models import VirtualMachine, VMInterface from . import filtersets, forms, tables from .choices import PrefixStatusChoices from .constants import * from .models import * -from .utils import add_requested_prefixes, add_available_ipaddresses, add_available_vlans +from .utils import add_requested_prefixes, add_available_vlans, annotate_ip_space # @@ -434,11 +433,6 @@ class AggregateBulkDeleteView(generic.BulkDeleteView): table = tables.AggregateTable -@register_model_view(Aggregate, 'contacts') -class AggregateContactsView(ObjectContactsView): - queryset = Aggregate.objects.all() - - # # Prefix/VLAN roles # @@ -625,7 +619,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView): class PrefixIPAddressesView(generic.ObjectChildrenView): queryset = Prefix.objects.all() child_model = IPAddress - table = tables.IPAddressTable + table = tables.AnnotatedIPAddressTable filterset = filtersets.IPAddressFilterSet filterset_form = forms.IPAddressFilterForm template_name = 'ipam/prefix/ip_addresses.html' @@ -641,7 +635,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView): def prep_table_data(self, request, queryset, parent): if not request.GET.get('q') and not get_table_ordering(request, self.table): - return add_available_ipaddresses(parent.prefix, queryset, parent.is_pool) + return annotate_ip_space(parent) return queryset def get_extra_context(self, request, instance): @@ -684,11 +678,6 @@ class PrefixBulkDeleteView(generic.BulkDeleteView): table = tables.PrefixTable -@register_model_view(Prefix, 'contacts') -class PrefixContactsView(ObjectContactsView): - queryset = Prefix.objects.all() - - # # IP Ranges # @@ -778,11 +767,6 @@ class IPRangeBulkDeleteView(generic.BulkDeleteView): table = tables.IPRangeTable -@register_model_view(IPRange, 'contacts') -class IPRangeContactsView(ObjectContactsView): - queryset = IPRange.objects.all() - - # # IP addresses # @@ -965,11 +949,6 @@ class IPAddressRelatedIPsView(generic.ObjectChildrenView): return parent.get_related_ips().restrict(request.user, 'view') -@register_model_view(IPAddress, 'contacts') -class IPAddressContactsView(ObjectContactsView): - queryset = IPAddress.objects.all() - - # # VLAN groups # @@ -1182,7 +1161,7 @@ class FHRPGroupListView(generic.ObjectListView): @register_model_view(FHRPGroup) -class FHRPGroupView(generic.ObjectView): +class FHRPGroupView(GetRelatedModelsMixin, generic.ObjectView): queryset = FHRPGroup.objects.all() def get_extra_context(self, request, instance): @@ -1194,6 +1173,18 @@ class FHRPGroupView(generic.ObjectView): members_table.columns.hide('group') return { + 'related_models': self.get_related_models( + request, instance, + extra=( + ( + Service.objects.restrict(request.user, 'view').filter( + parent_object_type=ContentType.objects.get_for_model(FHRPGroup), + parent_object_id=instance.id, + ), + 'fhrpgroup_id' + ), + ), + ), 'members_table': members_table, 'member_count': FHRPGroupAssignment.objects.filter(group=instance).count(), } @@ -1430,7 +1421,7 @@ class ServiceTemplateBulkDeleteView(generic.BulkDeleteView): @register_model_view(Service, 'list', path='', detail=False) class ServiceListView(generic.ObjectListView): - queryset = Service.objects.prefetch_related('device', 'virtual_machine') + queryset = Service.objects.prefetch_related('parent') filterset = filtersets.ServiceFilterSet filterset_form = forms.ServiceFilterForm table = tables.ServiceTable @@ -1440,6 +1431,18 @@ class ServiceListView(generic.ObjectListView): class ServiceView(generic.ObjectView): queryset = Service.objects.all() + def get_extra_context(self, request, instance): + context = {} + match instance.parent: + case Device(): + context['breadcrumb_queryparam'] = 'device_id' + case VirtualMachine(): + context['breadcrumb_queryparam'] = 'virtual_machine_id' + case FHRPGroup(): + context['breadcrumb_queryparam'] = 'fhrpgroup_id' + + return context + @register_model_view(Service, 'add', detail=False) class ServiceCreateView(generic.ObjectEditView): @@ -1466,7 +1469,7 @@ class ServiceBulkImportView(generic.BulkImportView): @register_model_view(Service, 'bulk_edit', path='edit', detail=False) class ServiceBulkEditView(generic.BulkEditView): - queryset = Service.objects.prefetch_related('device', 'virtual_machine') + queryset = Service.objects.prefetch_related('parent') filterset = filtersets.ServiceFilterSet table = tables.ServiceTable form = forms.ServiceBulkEditForm @@ -1474,11 +1477,6 @@ class ServiceBulkEditView(generic.BulkEditView): @register_model_view(Service, 'bulk_delete', path='delete', detail=False) class ServiceBulkDeleteView(generic.BulkDeleteView): - queryset = Service.objects.prefetch_related('device', 'virtual_machine') + queryset = Service.objects.prefetch_related('parent') filterset = filtersets.ServiceFilterSet table = tables.ServiceTable - - -@register_model_view(Service, 'contacts') -class ServiceContactsView(ObjectContactsView): - queryset = Service.objects.all() diff --git a/netbox/netbox/api/fields.py b/netbox/netbox/api/fields.py index e7d1ef574..db5ec184d 100644 --- a/netbox/netbox/api/fields.py +++ b/netbox/netbox/api/fields.py @@ -9,6 +9,7 @@ from rest_framework.exceptions import ValidationError from rest_framework.relations import PrimaryKeyRelatedField, RelatedField __all__ = ( + 'AttributesField', 'ChoiceField', 'ContentTypeField', 'IPNetworkSerializer', @@ -172,3 +173,19 @@ class IntegerRangeSerializer(serializers.Serializer): def to_representation(self, instance): return instance.lower, instance.upper - 1 + + +class AttributesField(serializers.JSONField): + """ + Custom attributes stored as JSON data. + """ + def to_internal_value(self, data): + data = super().to_internal_value(data) + + # If updating an object, start with the initial attribute data. This enables the client to modify + # individual attributes without having to rewrite the entire field. + if data and self.parent.instance: + initial_data = getattr(self.parent.instance, self.source, None) or {} + return {**initial_data, **data} + + return data diff --git a/netbox/netbox/api/views.py b/netbox/netbox/api/views.py index d58d1affe..1befda371 100644 --- a/netbox/netbox/api/views.py +++ b/netbox/netbox/api/views.py @@ -4,15 +4,15 @@ from django import __version__ as DJANGO_VERSION from django.apps import apps from django.conf import settings from django_rq.queues import get_connection -from drf_spectacular.utils import extend_schema from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.views import APIView from rq.worker import Worker -from netbox.plugins.utils import get_installed_plugins from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired +from netbox.plugins.utils import get_installed_plugins class APIRootView(APIView): @@ -66,7 +66,8 @@ class StatusView(APIView): return Response({ 'django-version': DJANGO_VERSION, 'installed-apps': installed_apps, - 'netbox-version': settings.RELEASE.full_version, + 'netbox-version': settings.RELEASE.version, + 'netbox-full-version': settings.RELEASE.full_version, 'plugins': get_installed_plugins(), 'python-version': platform.python_version(), 'rq-workers-running': Worker.count(get_connection('default')), diff --git a/netbox/netbox/api/viewsets/mixins.py b/netbox/netbox/api/viewsets/mixins.py index e07e2c78b..e21be2348 100644 --- a/netbox/netbox/api/viewsets/mixins.py +++ b/netbox/netbox/api/viewsets/mixins.py @@ -45,7 +45,7 @@ class ExportTemplatesMixin: if et is None: raise Http404 queryset = self.filter_queryset(self.get_queryset()) - return et.render_to_response(queryset) + return et.render_to_response(queryset=queryset) return super().list(request, *args, **kwargs) diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py index d9861545c..ada6b1293 100644 --- a/netbox/netbox/configuration_example.py +++ b/netbox/netbox/configuration_example.py @@ -12,14 +12,16 @@ ALLOWED_HOSTS = [] # PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: # https://docs.djangoproject.com/en/stable/ref/settings/#databases -DATABASE = { - 'ENGINE': 'django.db.backends.postgresql', # Database engine - 'NAME': 'netbox', # Database name - 'USER': '', # PostgreSQL username - 'PASSWORD': '', # PostgreSQL password - 'HOST': 'localhost', # Database server - 'PORT': '', # Database port (leave blank for default) - 'CONN_MAX_AGE': 300, # Max database connection age +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', # Database engine + 'NAME': 'netbox', # Database name + 'USER': '', # PostgreSQL username + 'PASSWORD': '', # PostgreSQL password + 'HOST': 'localhost', # Database server + 'PORT': '', # Database port (leave blank for default) + 'CONN_MAX_AGE': 300, # Max database connection age + } } # Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate @@ -164,6 +166,9 @@ LOGIN_REQUIRED = True # re-authenticate. (Default: 1209600 [14 days]) LOGIN_TIMEOUT = None +# Hide the login form. Useful when only allowing SSO authentication. +LOGIN_FORM_HIDDEN = False + # The view name or URL to which users are redirected after logging out. LOGOUT_REDIRECT_URL = 'home' diff --git a/netbox/netbox/configuration_testing.py b/netbox/netbox/configuration_testing.py index cec05cabb..52973e94d 100644 --- a/netbox/netbox/configuration_testing.py +++ b/netbox/netbox/configuration_testing.py @@ -5,13 +5,15 @@ ALLOWED_HOSTS = ['*'] -DATABASE = { - 'NAME': 'netbox', - 'USER': 'netbox', - 'PASSWORD': 'netbox', - 'HOST': 'localhost', - 'PORT': '', - 'CONN_MAX_AGE': 300, +DATABASES = { + 'default': { + 'NAME': 'netbox', + 'USER': 'netbox', + 'PASSWORD': 'netbox', + 'HOST': 'localhost', + 'PORT': '', + 'CONN_MAX_AGE': 300, + } } PLUGINS = [ @@ -41,6 +43,8 @@ SECRET_KEY = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' DEFAULT_PERMISSIONS = {} +ALLOW_TOKEN_RETRIEVAL = True + LOGGING = { 'version': 1, 'disable_existing_loggers': True diff --git a/netbox/netbox/filtersets.py b/netbox/netbox/filtersets.py index b8fbe7ad5..f24b4e11c 100644 --- a/netbox/netbox/filtersets.py +++ b/netbox/netbox/filtersets.py @@ -1,3 +1,5 @@ +import json + import django_filters from copy import deepcopy from django.contrib.contenttypes.models import ContentType @@ -10,7 +12,7 @@ from django.utils.translation import gettext as _ from core.choices import ObjectChangeActionChoices from core.models import ObjectChange from extras.choices import CustomFieldFilterLogicChoices -from extras.filters import TagFilter +from extras.filters import TagFilter, TagIDFilter from extras.models import CustomField, SavedFilter from utilities.constants import ( FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP, @@ -20,6 +22,7 @@ from utilities.forms.fields import MACAddressField from utilities import filters __all__ = ( + 'AttributeFiltersMixin', 'BaseFilterSet', 'ChangeLoggedModelFilterSet', 'NetBoxModelFilterSet', @@ -286,6 +289,7 @@ class NetBoxModelFilterSet(ChangeLoggedModelFilterSet): label=_('Search'), ) tag = TagFilter() + tag_id = TagIDFilter() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -329,3 +333,48 @@ class OrganizationalModelFilterSet(NetBoxModelFilterSet): models.Q(slug__icontains=value) | models.Q(description__icontains=value) ) + + +class NestedGroupModelFilterSet(NetBoxModelFilterSet): + """ + A base FilterSet for models that inherit from NestedGroupModel + """ + def search(self, queryset, name, value): + if value.strip(): + queryset = queryset.filter( + models.Q(name__icontains=value) | + models.Q(slug__icontains=value) | + models.Q(description__icontains=value) | + models.Q(comments__icontains=value) + ) + + return queryset + + +class AttributeFiltersMixin: + attributes_field_name = 'attribute_data' + attribute_filter_prefix = 'attr_' + + def __init__(self, data=None, queryset=None, *, request=None, prefix=None): + self.attr_filters = {} + + # Extract JSONField-based filters from the incoming data + if data is not None: + for key, value in data.items(): + if field := self._get_field_lookup(key): + # Attempt to cast the value to a native JSON type + try: + self.attr_filters[field] = json.loads(value) + except (ValueError, json.JSONDecodeError): + self.attr_filters[field] = value + + super().__init__(data=data, queryset=queryset, request=request, prefix=prefix) + + def _get_field_lookup(self, key): + if not key.startswith(self.attribute_filter_prefix): + return + lookup = key.split(self.attribute_filter_prefix, 1)[1] # Strip prefix + return f'{self.attributes_field_name}__{lookup}' + + def filter_queryset(self, queryset): + return super().filter_queryset(queryset).filter(**self.attr_filters) diff --git a/netbox/netbox/graphql/enums.py b/netbox/netbox/graphql/enums.py new file mode 100644 index 000000000..292a2e83e --- /dev/null +++ b/netbox/netbox/graphql/enums.py @@ -0,0 +1,13 @@ +import strawberry + +from netbox.choices import * + +__all__ = ( + 'ColorEnum', + 'DistanceUnitEnum', + 'WeightUnitEnum', +) + +ColorEnum = strawberry.enum(ColorChoices.as_enum(prefix='color')) +DistanceUnitEnum = strawberry.enum(DistanceUnitChoices.as_enum()) +WeightUnitEnum = strawberry.enum(WeightUnitChoices.as_enum()) diff --git a/netbox/netbox/graphql/filter_lookups.py b/netbox/netbox/graphql/filter_lookups.py new file mode 100644 index 000000000..859236e4d --- /dev/null +++ b/netbox/netbox/graphql/filter_lookups.py @@ -0,0 +1,219 @@ +from enum import Enum +from typing import TypeVar, Tuple, Generic + +import strawberry +import strawberry_django +from django.core.exceptions import FieldDoesNotExist +from django.db.models import Q, QuerySet +from django.db.models.fields.related import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel +from strawberry import ID +from strawberry.types import Info +from strawberry_django import ( + ComparisonFilterLookup, + DateFilterLookup, + DatetimeFilterLookup, + FilterLookup, + RangeLookup, + TimeFilterLookup, + process_filters, +) + +__all__ = ( + 'ArrayLookup', + 'FloatArrayLookup', + 'FloatLookup', + 'IntegerArrayLookup', + 'IntegerLookup', + 'JSONFilter', + 'StringArrayLookup', + 'TreeNodeFilter', +) + +T = TypeVar('T') +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.') +class JSONLookup: + string_lookup: FilterLookup[str] | None = strawberry_django.filter_field() + int_range_lookup: RangeLookup[int] | None = strawberry_django.filter_field() + int_comparison_lookup: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() + float_range_lookup: RangeLookup[float] | None = strawberry_django.filter_field() + float_comparison_lookup: ComparisonFilterLookup[float] | None = strawberry_django.filter_field() + date_lookup: DateFilterLookup[str] | None = strawberry_django.filter_field() + datetime_lookup: DatetimeFilterLookup[str] | None = strawberry_django.filter_field() + time_lookup: TimeFilterLookup[str] | None = strawberry_django.filter_field() + boolean_lookup: FilterLookup[bool] | None = strawberry_django.filter_field() + + def get_filter(self): + for field in self.__strawberry_definition__.fields: + value = getattr(self, field.name, None) + if value is not strawberry.UNSET: + return value + return None + + +@strawberry.input(one_of=True, description='Lookup for Integer fields. Only one of the lookup fields can be set.') +class IntegerLookup: + filter_lookup: FilterLookup[int] | None = strawberry_django.filter_field() + range_lookup: RangeLookup[int] | None = strawberry_django.filter_field() + comparison_lookup: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() + + def get_filter(self): + for field in self.__strawberry_definition__.fields: + value = getattr(self, field.name, None) + if value is not strawberry.UNSET: + return value + return None + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + filters = self.get_filter() + + if not filters: + return queryset, Q() + + return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix) + + +@strawberry.input(one_of=True, description='Lookup for Float fields. Only one of the lookup fields can be set.') +class FloatLookup: + filter_lookup: FilterLookup[float] | None = strawberry_django.filter_field() + range_lookup: RangeLookup[float] | None = strawberry_django.filter_field() + comparison_lookup: ComparisonFilterLookup[float] | None = strawberry_django.filter_field() + + def get_filter(self): + for field in self.__strawberry_definition__.fields: + value = getattr(self, field.name, None) + if value is not strawberry.UNSET: + return value + return None + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + filters = self.get_filter() + + if not filters: + return queryset, Q() + + return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix) + + +@strawberry.input +class JSONFilter: + """ + Class for JSON field lookups with paths + """ + + path: str + lookup: JSONLookup + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + filters = self.lookup.get_filter() + + if not filters: + return queryset, Q() + + json_path = f'{prefix}{self.path}__' + return process_filters(filters=filters, queryset=queryset, info=info, prefix=json_path) + + +@strawberry.enum +class TreeNodeMatch(Enum): + EXACT = 'exact' # Just the node itself + DESCENDANTS = 'descendants' # Node and all descendants + SELF_AND_DESCENDANTS = 'self_and_descendants' # Node and all descendants + CHILDREN = 'children' # Just immediate children + SIBLINGS = 'siblings' # Nodes with same parent + ANCESTORS = 'ancestors' # All parent nodes + PARENT = 'parent' # Just immediate parent + + +@strawberry.input +class TreeNodeFilter: + id: ID + match_type: TreeNodeMatch + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + model_field_name = prefix.removesuffix('__').removesuffix('_id') + model_field = None + try: + model_field = queryset.model._meta.get_field(model_field_name) + except FieldDoesNotExist: + try: + model_field = queryset.model._meta.get_field(f'{model_field_name}s') + except FieldDoesNotExist: + return queryset, Q(pk__in=[]) + + if hasattr(model_field, 'related_model'): + related_model = model_field.related_model + else: + return queryset, Q(pk__in=[]) + + # Generate base Q filter for the related model without prefix + q_filter = generate_tree_node_q_filter(related_model, self) + + # Handle different relationship types + if isinstance(model_field, (ManyToManyField, ManyToManyRel)): + return queryset, Q(**{f'{model_field_name}__in': related_model.objects.filter(q_filter)}) + elif isinstance(model_field, ForeignKey): + return queryset, Q(**{f'{model_field_name}__{k}': v for k, v in q_filter.children}) + elif isinstance(model_field, ManyToOneRel): + return queryset, Q(**{f'{model_field_name}__in': related_model.objects.filter(q_filter)}) + else: + return queryset, Q(**{f'{model_field_name}__{k}': v for k, v in q_filter.children}) + + +def generate_tree_node_q_filter(model_class, filter_value: TreeNodeFilter) -> Q: + """ + Generate appropriate Q filter for MPTT tree filtering based on match type + """ + try: + node = model_class.objects.get(id=filter_value.id) + except model_class.DoesNotExist: + return Q(pk__in=[]) + + if filter_value.match_type == TreeNodeMatch.EXACT: + return Q(id=filter_value.id) + elif filter_value.match_type == TreeNodeMatch.DESCENDANTS: + return Q(tree_id=node.tree_id, lft__gt=node.lft, rght__lt=node.rght) + elif filter_value.match_type == TreeNodeMatch.SELF_AND_DESCENDANTS: + return Q(tree_id=node.tree_id, lft__gte=node.lft, rght__lte=node.rght) + elif filter_value.match_type == TreeNodeMatch.CHILDREN: + return Q(tree_id=node.tree_id, level=node.level + 1, lft__gt=node.lft, rght__lt=node.rght) + elif filter_value.match_type == TreeNodeMatch.SIBLINGS: + return Q(tree_id=node.tree_id, level=node.level, parent=node.parent) & ~Q(id=node.id) + elif filter_value.match_type == TreeNodeMatch.ANCESTORS: + return Q(tree_id=node.tree_id, lft__lt=node.lft, rght__gt=node.rght) + elif filter_value.match_type == TreeNodeMatch.PARENT: + return Q(id=node.parent_id) if node.parent_id else Q(pk__in=[]) + return Q() + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class ArrayLookup(Generic[T]): + """ + Class for Array field lookups + """ + + contains: list[T] | None = strawberry_django.filter_field(description='Contains the value') + contained_by: list[T] | None = strawberry_django.filter_field(description='Contained by the value') + overlap: list[T] | None = strawberry_django.filter_field(description='Overlaps with the value') + length: int | None = strawberry_django.filter_field(description='Length of the array') + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class IntegerArrayLookup(ArrayLookup[int]): + pass + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class FloatArrayLookup(ArrayLookup[float]): + pass + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class StringArrayLookup(ArrayLookup[str]): + pass diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index 2044a1dde..b77238325 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -1,209 +1,104 @@ -from functools import partialmethod -from typing import List +from dataclasses import dataclass +from datetime import datetime +from typing import TypeVar, TYPE_CHECKING, Annotated -import django_filters import strawberry import strawberry_django -from django.core.exceptions import FieldDoesNotExist -from strawberry import auto +from strawberry import ID +from strawberry_django import FilterLookup, DatetimeFilterLookup -from ipam.fields import ASNField -from netbox.graphql.scalars import BigInt -from utilities.fields import ColorField, CounterCacheField -from utilities.filters import * +from core.graphql.filter_mixins import BaseFilterMixin, BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin + +__all__ = ( + 'DistanceFilterMixin', + 'ImageAttachmentFilterMixin', + 'NestedGroupModelFilterMixin', + 'NetBoxModelFilterMixin', + 'OrganizationalModelFilterMixin', + 'PrimaryModelFilterMixin', + 'SyncedDataFilterMixin', + 'WeightFilterMixin', +) + +T = TypeVar('T') -def map_strawberry_type(field): - should_create_function = False - attr_type = None - - # NetBox Filter types - put base classes after derived classes - if isinstance(field, ContentTypeFilter): - should_create_function = True - attr_type = str | None - elif isinstance(field, MultiValueArrayFilter): - pass - elif isinstance(field, MultiValueCharFilter): - # Note: Need to use the legacy FilterLookup from filters, not from - # strawberry_django.FilterLookup as we currently have USE_DEPRECATED_FILTERS - attr_type = strawberry_django.filters.FilterLookup[str] | None - elif isinstance(field, MultiValueDateFilter): - attr_type = auto - elif isinstance(field, MultiValueDateTimeFilter): - attr_type = auto - elif isinstance(field, MultiValueDecimalFilter): - pass - elif isinstance(field, MultiValueMACAddressFilter): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, MultiValueNumberFilter): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, MultiValueTimeFilter): - pass - elif isinstance(field, MultiValueWWNFilter): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, NullableCharFieldFilter): - pass - elif isinstance(field, NumericArrayFilter): - should_create_function = True - attr_type = int | None - elif isinstance(field, TreeNodeMultipleChoiceFilter): - should_create_function = True - attr_type = List[str] | None - - # From django_filters - ordering of these matters as base classes must - # come after derived classes so the base class doesn't get matched first - # a pass for the check (no attr_type) means we don't currently handle - # or use that type - elif issubclass(type(field), django_filters.OrderingFilter): - pass - elif issubclass(type(field), django_filters.BaseRangeFilter): - pass - elif issubclass(type(field), django_filters.BaseInFilter): - pass - elif issubclass(type(field), django_filters.LookupChoiceFilter): - pass - elif issubclass(type(field), django_filters.AllValuesMultipleFilter): - pass - elif issubclass(type(field), django_filters.AllValuesFilter): - pass - elif issubclass(type(field), django_filters.TimeRangeFilter): - pass - elif issubclass(type(field), django_filters.IsoDateTimeFromToRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DateTimeFromToRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DateFromToRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DateRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.RangeFilter): - pass - elif issubclass(type(field), django_filters.NumericRangeFilter): - pass - elif issubclass(type(field), django_filters.NumberFilter): - should_create_function = True - attr_type = int | None - elif issubclass(type(field), django_filters.ModelMultipleChoiceFilter): - should_create_function = True - attr_type = List[str] | None - elif issubclass(type(field), django_filters.ModelChoiceFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DurationFilter): - pass - elif issubclass(type(field), django_filters.IsoDateTimeFilter): - pass - elif issubclass(type(field), django_filters.DateTimeFilter): - attr_type = auto - elif issubclass(type(field), django_filters.TimeFilter): - attr_type = auto - elif issubclass(type(field), django_filters.DateFilter): - attr_type = auto - elif issubclass(type(field), django_filters.TypedMultipleChoiceFilter): - pass - elif issubclass(type(field), django_filters.MultipleChoiceFilter): - attr_type = str | None - elif issubclass(type(field), django_filters.TypedChoiceFilter): - pass - elif issubclass(type(field), django_filters.ChoiceFilter): - pass - elif issubclass(type(field), django_filters.BooleanFilter): - should_create_function = True - attr_type = bool | None - elif issubclass(type(field), django_filters.UUIDFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.CharFilter): - # looks like only used by 'q' - should_create_function = True - attr_type = str | None - - return should_create_function, attr_type +if TYPE_CHECKING: + from .enums import * + from core.graphql.filters import * + from extras.graphql.filters import * -def autotype_decorator(filterset): - """ - Decorator used to auto creates a dataclass used by Strawberry based on a filterset. - Must go after the Strawberry decorator as follows: - - @strawberry_django.filter(models.Example, lookups=True) - @autotype_decorator(filtersets.ExampleFilterSet) - class ExampleFilter(BaseFilterMixin): - pass - - The Filter itself must be derived from BaseFilterMixin. For items listed in meta.fields - of the filterset, usually just a type specifier is generated, so for - `fields = [created, ]` the dataclass would be: - - class ExampleFilter(BaseFilterMixin): - created: auto - - For other filter fields a function needs to be created for Strawberry with the - naming convention `filter_{fieldname}` which is auto detected and called by - Strawberry, this function uses the filterset to handle the query. - """ - def create_attribute_and_function(cls, fieldname, attr_type, should_create_function): - if fieldname not in cls.__annotations__ and attr_type: - cls.__annotations__[fieldname] = attr_type - - filter_name = f"filter_{fieldname}" - if should_create_function and not hasattr(cls, filter_name): - filter_by_filterset = getattr(cls, 'filter_by_filterset') - setattr(cls, filter_name, partialmethod(filter_by_filterset, key=fieldname)) - - def wrapper(cls): - cls.filterset = filterset - fields = filterset.get_fields() - model = filterset._meta.model - for fieldname in fields.keys(): - should_create_function = False - attr_type = auto - if fieldname not in cls.__annotations__: - try: - field = model._meta.get_field(fieldname) - except FieldDoesNotExist: - continue - - if isinstance(field, CounterCacheField): - should_create_function = True - attr_type = BigInt | None - elif isinstance(field, ASNField): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, ColorField): - should_create_function = True - attr_type = List[str] | None - - create_attribute_and_function(cls, fieldname, attr_type, should_create_function) - - declared_filters = filterset.declared_filters - for fieldname, field in declared_filters.items(): - - should_create_function, attr_type = map_strawberry_type(field) - if attr_type is None: - raise NotImplementedError(f"GraphQL Filter field unknown: {fieldname}: {field}") - - create_attribute_and_function(cls, fieldname, attr_type, should_create_function) - - return cls - - return wrapper +class NetBoxModelFilterMixin( + ChangeLogFilterMixin, + CustomFieldsFilterMixin, + JournalEntriesFilterMixin, + TagsFilterMixin, + BaseObjectTypeFilterMixin, +): + pass -@strawberry.input -class BaseFilterMixin: +@dataclass +class NestedGroupModelFilterMixin(NetBoxModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + parent_id: ID | None = strawberry_django.filter_field() - def filter_by_filterset(self, queryset, key): - filterset = self.filterset(data={key: getattr(self, key)}, queryset=queryset) - if not filterset.is_valid(): - # We could raise validation error but strawberry logs it all to the - # console i.e. raise ValidationError(f"{k}: {v[0]}") - return filterset.qs.none() - return filterset.qs + +@dataclass +class OrganizationalModelFilterMixin( + ChangeLogFilterMixin, + CustomFieldsFilterMixin, + TagsFilterMixin, + BaseObjectTypeFilterMixin, +): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class PrimaryModelFilterMixin(NetBoxModelFilterMixin): + description: FilterLookup[str] | None = strawberry_django.filter_field() + comments: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class ImageAttachmentFilterMixin(BaseFilterMixin): + images: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class WeightFilterMixin(BaseFilterMixin): + weight: FilterLookup[float] | None = strawberry_django.filter_field() + weight_unit: Annotated['WeightUnitEnum', strawberry.lazy('netbox.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class SyncedDataFilterMixin(BaseFilterMixin): + data_source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + data_source_id: FilterLookup[int] | None = strawberry_django.filter_field() + data_file: Annotated['DataFileFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + data_file_id: FilterLookup[int] | None = strawberry_django.filter_field() + data_path: FilterLookup[str] | None = strawberry_django.filter_field() + auto_sync_enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + data_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + + +@dataclass +class DistanceFilterMixin(BaseFilterMixin): + distance: FilterLookup[float] | None = strawberry_django.filter_field() + distance_unit: Annotated['DistanceUnitEnum', strawberry.lazy('netbox.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/netbox/graphql/schema.py b/netbox/netbox/graphql/schema.py index a7609c9d2..c840e769c 100644 --- a/netbox/netbox/graphql/schema.py +++ b/netbox/netbox/graphql/schema.py @@ -1,7 +1,7 @@ import strawberry from django.conf import settings from strawberry_django.optimizer import DjangoOptimizerExtension -from strawberry.extensions import MaxAliasesLimiter +from strawberry.extensions import MaxAliasesLimiter # , SchemaExtension from strawberry.schema.config import StrawberryConfig from circuits.graphql.schema import CircuitsQuery diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py index a4fc99080..653462630 100644 --- a/netbox/netbox/graphql/types.py +++ b/netbox/netbox/graphql/types.py @@ -8,6 +8,7 @@ from extras.graphql.mixins import CustomFieldsMixin, JournalEntriesMixin, TagsMi __all__ = ( 'BaseObjectType', + 'ContentTypeType', 'ObjectType', 'OrganizationalObjectType', 'NetBoxObjectType', @@ -83,6 +84,7 @@ class NetBoxObjectType( @strawberry_django.type( ContentType, fields=['id', 'app_label', 'model'], + pagination=True ) class ContentTypeType: pass @@ -91,6 +93,7 @@ class ContentTypeType: @strawberry_django.type( ObjectType_, fields=['id', 'app_label', 'model'], + pagination=True ) class ObjectTypeType: pass diff --git a/netbox/netbox/middleware.py b/netbox/netbox/middleware.py index d53f67803..3a9cb0f78 100644 --- a/netbox/netbox/middleware.py +++ b/netbox/netbox/middleware.py @@ -2,6 +2,7 @@ from contextlib import ExitStack import logging import uuid +import warnings from django.conf import settings from django.contrib import auth, messages @@ -37,7 +38,10 @@ class CoreMiddleware: # Apply all registered request processors with ExitStack() as stack: for request_processor in registry['request_processors']: - stack.enter_context(request_processor(request)) + try: + stack.enter_context(request_processor(request)) + except Exception as e: + warnings.warn(f'Failed to initialize request processor {request_processor}: {e}') response = self.get_response(request) # Check if language cookie should be renewed @@ -99,18 +103,23 @@ class RemoteUserMiddleware(RemoteUserMiddleware_): """ Custom implementation of Django's RemoteUserMiddleware which allows for a user-configurable HTTP header name. """ + async_capable = False force_logout_if_no_header = False + def __init__(self, get_response): + if get_response is None: + raise ValueError("get_response must be provided.") + self.get_response = get_response + @property def header(self): return settings.REMOTE_AUTH_HEADER - def process_request(self, request): - logger = logging.getLogger( - 'netbox.authentication.RemoteUserMiddleware') + def __call__(self, request): + logger = logging.getLogger('netbox.authentication.RemoteUserMiddleware') # Bypass middleware if remote authentication is not enabled if not settings.REMOTE_AUTH_ENABLED: - return + return self.get_response(request) # AuthenticationMiddleware is required so that request.user exists. if not hasattr(request, 'user'): raise ImproperlyConfigured( @@ -127,13 +136,13 @@ class RemoteUserMiddleware(RemoteUserMiddleware_): # AnonymousUser by the AuthenticationMiddleware). if self.force_logout_if_no_header and request.user.is_authenticated: self._remove_invalid_user(request) - return + return self.get_response(request) # If the user is already authenticated and that user is the user we are # getting passed in the headers, then the correct user is already # persisted in the session and we don't need to continue. if request.user.is_authenticated: if request.user.get_username() == self.clean_username(username, request): - return + return self.get_response(request) else: # An authenticated user is associated with the request, but # it does not match the authorized user in the header. @@ -163,6 +172,8 @@ class RemoteUserMiddleware(RemoteUserMiddleware_): request.user = user auth.login(request, user) + return self.get_response(request) + def _get_groups(self, request): logger = logging.getLogger( 'netbox.authentication.RemoteUserMiddleware') diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index ca79d5e7e..f07f75736 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -151,6 +151,10 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel): max_length=200, blank=True ) + comments = models.TextField( + verbose_name=_('comments'), + blank=True + ) objects = TreeManager() diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index b58d232cd..25f23c9d3 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -5,6 +5,7 @@ from functools import cached_property from django.contrib.contenttypes.fields import GenericRelation from django.core.validators import ValidationError from django.db import models +from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from taggit.managers import TaggableManager @@ -19,7 +20,6 @@ from netbox.registry import registry from netbox.signals import post_clean from utilities.json import CustomFieldJSONEncoder from utilities.serialization import serialize_object -from utilities.views import register_model_view __all__ = ( 'BookmarksMixin', @@ -360,7 +360,7 @@ class ImageAttachmentsMixin(models.Model): class ContactsMixin(models.Model): """ - Enables the assignments of Contacts (via ContactAssignment). + Enables the assignment of Contacts to a model (via ContactAssignment). """ contacts = GenericRelation( to='tenancy.ContactAssignment', @@ -371,6 +371,27 @@ class ContactsMixin(models.Model): class Meta: abstract = True + def get_contacts(self, inherited=True): + """ + Return a `QuerySet` matching all contacts assigned to this object. + + Args: + inherited: If `True`, inherited contacts from parent objects are included. + """ + from tenancy.models import ContactAssignment + from . import NestedGroupModel + + filter = Q( + object_type=ObjectType.objects.get_for_model(self), + object_id__in=( + self.get_ancestors(include_self=True) + if (isinstance(self, NestedGroupModel) and inherited) + else [self.pk] + ), + ) + + return ContactAssignment.objects.filter(filter) + class BookmarksMixin(models.Model): """ @@ -442,7 +463,8 @@ class TagsMixin(models.Model): which is a `TaggableManager` instance. """ tags = TaggableManager( - through='extras.TaggedItem' + through='extras.TaggedItem', + ordering=('weight', 'name'), ) class Meta: @@ -625,6 +647,8 @@ def register_models(*models): register_model() should be called for each relevant model under the ready() of an app's AppConfig class. """ + from utilities.views import register_model_view + for model in models: app_label, model_name = model._meta.label_lower.split('.') @@ -645,6 +669,10 @@ def register_models(*models): ) # Register applicable feature views for the model + if issubclass(model, ContactsMixin): + register_model_view(model, 'contacts', kwargs={'model': model})( + 'netbox.views.generic.ObjectContactsView' + ) if issubclass(model, JournalingMixin): register_model_view(model, 'journal', kwargs={'model': model})( 'netbox.views.generic.ObjectJournalView' diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 9148caa8e..982d40829 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -14,9 +14,9 @@ ORGANIZATION_MENU = Menu( MenuGroup( label=_('Sites'), items=( - get_model_item('dcim', 'site', _('Sites')), get_model_item('dcim', 'region', _('Regions')), get_model_item('dcim', 'sitegroup', _('Site Groups')), + get_model_item('dcim', 'site', _('Sites')), get_model_item('dcim', 'location', _('Locations')), ), ), @@ -85,6 +85,7 @@ DEVICES_MENU = Menu( items=( get_model_item('dcim', 'devicetype', _('Device Types')), get_model_item('dcim', 'moduletype', _('Module Types')), + get_model_item('dcim', 'moduletypeprofile', _('Module Type Profiles')), get_model_item('dcim', 'manufacturer', _('Manufacturers')), ), ), @@ -348,6 +349,7 @@ CUSTOMIZATION_MENU = Menu( get_model_item('extras', 'customlink', _('Custom Links')), get_model_item('extras', 'exporttemplate', _('Export Templates')), get_model_item('extras', 'savedfilter', _('Saved Filters')), + get_model_item('extras', 'tableconfig', _('Table Configs'), actions=()), get_model_item('extras', 'tag', 'Tags'), get_model_item('extras', 'imageattachment', _('Image Attachments'), actions=()), ), diff --git a/netbox/netbox/plugins/__init__.py b/netbox/netbox/plugins/__init__.py index 69881a251..b7bb0ef9f 100644 --- a/netbox/netbox/plugins/__init__.py +++ b/netbox/netbox/plugins/__init__.py @@ -6,6 +6,7 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.module_loading import import_string from packaging import version +from core.exceptions import IncompatiblePluginError from netbox.registry import registry from netbox.search import register_search from netbox.utils import register_data_backend @@ -16,6 +17,7 @@ from .utils import * # Initialize plugin registry registry['plugins'].update({ + 'installed': [], 'graphql_schemas': [], 'menus': [], 'menu_items': {}, @@ -47,6 +49,7 @@ class PluginConfig(AppConfig): author_email = '' description = '' version = '' + release_track = '' # Root URL path under /plugins. If not set, the plugin's label will be used. base_url = None @@ -138,14 +141,14 @@ class PluginConfig(AppConfig): if cls.min_version is not None: min_version = version.parse(cls.min_version) if current_version < min_version: - raise ImproperlyConfigured( + raise IncompatiblePluginError( f"Plugin {cls.__module__} requires NetBox minimum version {cls.min_version} (current: " f"{netbox_version})." ) if cls.max_version is not None: max_version = version.parse(cls.max_version) if current_version > max_version: - raise ImproperlyConfigured( + raise IncompatiblePluginError( f"Plugin {cls.__module__} requires NetBox maximum version {cls.max_version} (current: " f"{netbox_version})." ) diff --git a/netbox/netbox/plugins/registration.py b/netbox/netbox/plugins/registration.py index 515405f1b..0001d50c9 100644 --- a/netbox/netbox/plugins/registration.py +++ b/netbox/netbox/plugins/registration.py @@ -1,7 +1,7 @@ import inspect -import warnings from django.utils.translation import gettext_lazy as _ + from netbox.registry import registry from .navigation import PluginMenu, PluginMenuButton, PluginMenuItem from .templates import PluginTemplateExtension @@ -35,16 +35,8 @@ def register_template_extensions(class_list): ) if template_extension.models: - # Registration for multiple models + # Registration for specific models models = template_extension.models - elif template_extension.model: - # Registration for a single model (deprecated) - warnings.warn( - "PluginTemplateExtension.model is deprecated and will be removed in a future release. Use " - "'models' instead.", - DeprecationWarning - ) - models = [template_extension.model] else: # Global registration (no specific models) models = [None] diff --git a/netbox/netbox/plugins/templates.py b/netbox/netbox/plugins/templates.py index 4ea90b4db..586391d4f 100644 --- a/netbox/netbox/plugins/templates.py +++ b/netbox/netbox/plugins/templates.py @@ -11,8 +11,14 @@ class PluginTemplateExtension: This class is used to register plugin content to be injected into core NetBox templates. It contains methods that are overridden by plugin authors to return template content. - The `model` attribute on the class defines the which model detail page this class renders content for. It - should be set as a string in the form '.'. render() provides the following context data: + The `models` attribute on the class defines the which specific model detail pages this class renders content + for. It should be defined as a list of strings in the following form: + + models = ['.', '.'] + + If `models` is left as None, the extension will render for _all_ models. + + The `render()` method provides the following context data: * object - The object being viewed (object views only) * model - The type of object being viewed (list views only) @@ -21,7 +27,6 @@ class PluginTemplateExtension: * config - Plugin-specific configuration parameters """ models = None - model = None # Deprecated; use `models` instead def __init__(self, context): self.context = context @@ -42,6 +47,13 @@ class PluginTemplateExtension: # Global methods # + def head(self): + """ + HTML returned by this method will be inserted in the page's `` block. This may be useful e.g. for + including additional Javascript or CSS resources. + """ + raise NotImplementedError + def navbar(self): """ Content that will be rendered inside the top navigation menu. Content should be returned as an HTML diff --git a/netbox/netbox/plugins/urls.py b/netbox/netbox/plugins/urls.py index 7a9f30c7e..791c1d7b5 100644 --- a/netbox/netbox/plugins/urls.py +++ b/netbox/netbox/plugins/urls.py @@ -1,11 +1,11 @@ from importlib import import_module from django.apps import apps -from django.conf import settings from django.conf.urls import include from django.urls import path from django.utils.module_loading import import_string, module_has_submodule +from netbox.registry import registry from . import views plugin_patterns = [] @@ -15,7 +15,7 @@ plugin_api_patterns = [ ] # Register base/API URL patterns for each plugin -for plugin_path in settings.PLUGINS: +for plugin_path in registry['plugins']['installed']: plugin = import_module(plugin_path) plugin_name = plugin_path.split('.')[-1] app = apps.get_app_config(plugin_name) diff --git a/netbox/netbox/plugins/utils.py b/netbox/netbox/plugins/utils.py index c260f156d..886292274 100644 --- a/netbox/netbox/plugins/utils.py +++ b/netbox/netbox/plugins/utils.py @@ -2,6 +2,8 @@ from django.apps import apps from django.conf import settings from django.core.exceptions import ImproperlyConfigured +from netbox.registry import registry + __all__ = ( 'get_installed_plugins', 'get_plugin_config', @@ -13,10 +15,13 @@ def get_installed_plugins(): Return a dictionary mapping the names of installed plugins to their versions. """ plugins = {} - for plugin_name in settings.PLUGINS: + for plugin_name in registry['plugins']['installed']: plugin_name = plugin_name.rsplit('.', 1)[-1] plugin_config = apps.get_app_config(plugin_name) - plugins[plugin_name] = getattr(plugin_config, 'version', None) + if plugin_config.release_track: + plugins[plugin_name] = f'{plugin_config.version}-{plugin_config.release_track}' + else: + plugins[plugin_name] = plugin_config.version or None return dict(sorted(plugins.items())) diff --git a/netbox/netbox/plugins/views.py b/netbox/netbox/plugins/views.py index 6a10f2e2c..feee78e82 100644 --- a/netbox/netbox/plugins/views.py +++ b/netbox/netbox/plugins/views.py @@ -1,7 +1,6 @@ from collections import OrderedDict from django.apps import apps -from django.conf import settings from django.urls.exceptions import NoReverseMatch from drf_spectacular.utils import extend_schema from rest_framework import permissions @@ -9,6 +8,8 @@ from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.views import APIView +from netbox.registry import registry + @extend_schema(exclude=True) class InstalledPluginsAPIView(APIView): @@ -30,11 +31,15 @@ class InstalledPluginsAPIView(APIView): 'author': plugin_app_config.author, 'author_email': plugin_app_config.author_email, 'description': plugin_app_config.description, - 'version': plugin_app_config.version + 'version': plugin_app_config.version, + 'release_track': plugin_app_config.release_track, } def get(self, request, format=None): - return Response([self._get_plugin_data(apps.get_app_config(plugin)) for plugin in settings.PLUGINS]) + return Response([ + self._get_plugin_data(apps.get_app_config(plugin)) + for plugin in registry['plugins']['installed'] + ]) @extend_schema(exclude=True) @@ -64,7 +69,7 @@ class PluginsAPIRootView(APIView): def get(self, request, format=None): entries = [] - for plugin in settings.PLUGINS: + for plugin in registry['plugins']['installed']: app_config = apps.get_app_config(plugin) entry = self._get_plugin_entry(plugin, app_config, request, format) if entry is not None: diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index dbbac6ec4..239b5978f 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -9,11 +9,15 @@ import warnings from django.contrib.messages import constants as messages from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.validators import URLValidator +from django.utils.module_loading import import_string from django.utils.translation import gettext_lazy as _ +from core.exceptions import IncompatiblePluginError from netbox.config import PARAMS as CONFIG_PARAMS from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW from netbox.plugins import PluginConfig +from netbox.registry import registry +import storages.utils # type: ignore from utilities.release import load_release_data from utilities.string import trailing_slash @@ -49,14 +53,18 @@ except ModuleNotFoundError as e: ) raise -# Check for missing required configuration parameters -for parameter in ('ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY', 'REDIS'): +# Check for missing/conflicting required configuration parameters +for parameter in ('ALLOWED_HOSTS', 'SECRET_KEY', 'REDIS'): if not hasattr(configuration, parameter): raise ImproperlyConfigured(f"Required parameter {parameter} is missing from configuration.") +if not hasattr(configuration, 'DATABASE') and not hasattr(configuration, 'DATABASES'): + raise ImproperlyConfigured("The database configuration must be defined using DATABASE or DATABASES.") +elif hasattr(configuration, 'DATABASE') and hasattr(configuration, 'DATABASES'): + raise ImproperlyConfigured("DATABASE and DATABASES may not be set together. The use of DATABASES is encouraged.") # Set static config parameters ADMINS = getattr(configuration, 'ADMINS', []) -ALLOW_TOKEN_RETRIEVAL = getattr(configuration, 'ALLOW_TOKEN_RETRIEVAL', True) +ALLOW_TOKEN_RETRIEVAL = getattr(configuration, 'ALLOW_TOKEN_RETRIEVAL', False) ALLOWED_HOSTS = getattr(configuration, 'ALLOWED_HOSTS') # Required AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [ { @@ -80,7 +88,9 @@ CSRF_COOKIE_PATH = f'/{BASE_PATH.rstrip("/")}' CSRF_COOKIE_SECURE = getattr(configuration, 'CSRF_COOKIE_SECURE', False) CSRF_TRUSTED_ORIGINS = getattr(configuration, 'CSRF_TRUSTED_ORIGINS', []) DATA_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'DATA_UPLOAD_MAX_MEMORY_SIZE', 2621440) -DATABASE = getattr(configuration, 'DATABASE') # Required +DATABASE = getattr(configuration, 'DATABASE', None) # Legacy DB definition +DATABASE_ROUTERS = getattr(configuration, 'DATABASE_ROUTERS', []) +DATABASES = getattr(configuration, 'DATABASES', {'default': DATABASE}) DEBUG = getattr(configuration, 'DEBUG', False) DEFAULT_DASHBOARD = getattr(configuration, 'DEFAULT_DASHBOARD', None) DEFAULT_PERMISSIONS = getattr(configuration, 'DEFAULT_PERMISSIONS', { @@ -115,7 +125,7 @@ EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', []) FIELD_CHOICES = getattr(configuration, 'FIELD_CHOICES', {}) FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'FILE_UPLOAD_MAX_MEMORY_SIZE', 2621440) GRAPHQL_MAX_ALIASES = getattr(configuration, 'GRAPHQL_MAX_ALIASES', 10) -HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', None) +HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', {}) INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1')) ISOLATED_DEPLOYMENT = getattr(configuration, 'ISOLATED_DEPLOYMENT', False) JINJA2_FILTERS = getattr(configuration, 'JINJA2_FILTERS', {}) @@ -125,11 +135,14 @@ LOGGING = getattr(configuration, 'LOGGING', {}) LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False) LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', True) LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None) +LOGIN_FORM_HIDDEN = getattr(configuration, 'LOGIN_FORM_HIDDEN', False) LOGOUT_REDIRECT_URL = getattr(configuration, 'LOGOUT_REDIRECT_URL', 'home') MEDIA_ROOT = getattr(configuration, 'MEDIA_ROOT', os.path.join(BASE_DIR, 'media')).rstrip('/') METRICS_ENABLED = getattr(configuration, 'METRICS_ENABLED', False) PLUGINS = getattr(configuration, 'PLUGINS', []) PLUGINS_CONFIG = getattr(configuration, 'PLUGINS_CONFIG', {}) +PLUGINS_CATALOG_CONFIG = getattr(configuration, 'PLUGINS_CATALOG_CONFIG', {}) +PROXY_ROUTERS = getattr(configuration, 'PROXY_ROUTERS', ['utilities.proxy.DefaultProxyRouter']) QUEUE_MAPPINGS = getattr(configuration, 'QUEUE_MAPPINGS', {}) REDIS = getattr(configuration, 'REDIS') # Required RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None) @@ -173,7 +186,8 @@ SESSION_COOKIE_PATH = CSRF_COOKIE_PATH SESSION_COOKIE_SECURE = getattr(configuration, 'SESSION_COOKIE_SECURE', False) SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None) STORAGE_BACKEND = getattr(configuration, 'STORAGE_BACKEND', None) -STORAGE_CONFIG = getattr(configuration, 'STORAGE_CONFIG', {}) +STORAGE_CONFIG = getattr(configuration, 'STORAGE_CONFIG', None) +STORAGES = getattr(configuration, 'STORAGES', {}) TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC') TRANSLATION_ENABLED = getattr(configuration, 'TRANSLATION_ENABLED', True) DISK_BASE_UNIT = getattr(configuration, 'DISK_BASE_UNIT', 1000) @@ -206,83 +220,92 @@ if RELEASE_CHECK_URL: "RELEASE_CHECK_URL must be a valid URL. Example: https://api.github.com/repos/netbox-community/netbox" ) +# Validate configured proxy routers +for path in PROXY_ROUTERS: + if type(path) is str: + try: + import_string(path) + except ImportError: + raise ImproperlyConfigured(f"Invalid path in PROXY_ROUTERS: {path}") + # # Database # -# Set the database engine -if 'ENGINE' not in DATABASE: - if METRICS_ENABLED: - DATABASE.update({'ENGINE': 'django_prometheus.db.backends.postgresql'}) - else: - DATABASE.update({'ENGINE': 'django.db.backends.postgresql'}) +# Verify that a default database has been configured +if 'default' not in DATABASES: + raise ImproperlyConfigured("No default database has been configured.") -# Define the DATABASES setting for Django -DATABASES = { - 'default': DATABASE, -} +# Set the database engine +if 'ENGINE' not in DATABASES['default']: + DATABASES['default'].update({ + 'ENGINE': 'django_prometheus.db.backends.postgresql' if METRICS_ENABLED else 'django.db.backends.postgresql' + }) # # Storage backend # +if STORAGE_BACKEND is not None: + if not STORAGES: + raise ImproperlyConfigured( + "STORAGE_BACKEND and STORAGES are both set, remove the deprecated STORAGE_BACKEND setting." + ) + else: + warnings.warn( + "STORAGE_BACKEND is deprecated, use the new STORAGES setting instead." + ) + +if STORAGE_CONFIG is not None: + warnings.warn( + "STORAGE_CONFIG is deprecated, use the new STORAGES setting instead." + ) + # Default STORAGES for Django -STORAGES = { +DEFAULT_STORAGES = { "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", }, "staticfiles": { "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", }, + "scripts": { + "BACKEND": "extras.storage.ScriptFileSystemStorage", + }, } +STORAGES = DEFAULT_STORAGES | STORAGES +# TODO: This code is deprecated and needs to be removed in the future if STORAGE_BACKEND is not None: STORAGES['default']['BACKEND'] = STORAGE_BACKEND - # django-storages - if STORAGE_BACKEND.startswith('storages.'): - try: - import storages.utils # type: ignore - except ModuleNotFoundError as e: - if getattr(e, 'name') == 'storages': - raise ImproperlyConfigured( - f"STORAGE_BACKEND is set to {STORAGE_BACKEND} but django-storages is not present. It can be " - f"installed by running 'pip install django-storages'." - ) - raise e +# Monkey-patch django-storages to fetch settings from STORAGE_CONFIG +if STORAGE_CONFIG is not None: + def _setting(name, default=None): + if name in STORAGE_CONFIG: + return STORAGE_CONFIG[name] + return globals().get(name, default) + storages.utils.setting = _setting - # Monkey-patch django-storages to fetch settings from STORAGE_CONFIG - def _setting(name, default=None): - if name in STORAGE_CONFIG: - return STORAGE_CONFIG[name] - return globals().get(name, default) - storages.utils.setting = _setting - - # django-storage-swift - elif STORAGE_BACKEND == 'swift.storage.SwiftStorage': - try: - import swift.utils # noqa: F401 - except ModuleNotFoundError as e: - if getattr(e, 'name') == 'swift': - raise ImproperlyConfigured( - f"STORAGE_BACKEND is set to {STORAGE_BACKEND} but django-storage-swift is not present. " - "It can be installed by running 'pip install django-storage-swift'." - ) - raise e - - # Load all SWIFT_* settings from the user configuration - for param, value in STORAGE_CONFIG.items(): - if param.startswith('SWIFT_'): - globals()[param] = value - -if STORAGE_CONFIG and STORAGE_BACKEND is None: - warnings.warn( - "STORAGE_CONFIG has been set in configuration.py but STORAGE_BACKEND is not defined. STORAGE_CONFIG will be " - "ignored." - ) +# django-storage-swift +if STORAGE_BACKEND == 'swift.storage.SwiftStorage': + try: + import swift.utils # noqa: F401 + except ModuleNotFoundError as e: + if getattr(e, 'name') == 'swift': + raise ImproperlyConfigured( + f"STORAGE_BACKEND is set to {STORAGE_BACKEND} but django-storage-swift is not present. " + "It can be installed by running 'pip install django-storage-swift'." + ) + raise e + # Load all SWIFT_* settings from the user configuration + for param, value in STORAGE_CONFIG.items(): + if param.startswith('SWIFT_'): + globals()[param] = value +# TODO: End of deprecated code # # Redis @@ -582,6 +605,7 @@ if SENTRY_ENABLED: sample_rate=SENTRY_SAMPLE_RATE, traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE, send_default_pii=SENTRY_SEND_DEFAULT_PII, + # TODO: Support proxy routing http_proxy=HTTP_PROXIES.get('http') if HTTP_PROXIES else None, https_proxy=HTTP_PROXIES.get('https') if HTTP_PROXIES else None ) @@ -781,7 +805,7 @@ LOCALE_PATHS = ( STRAWBERRY_DJANGO = { "DEFAULT_PK_FIELD_NAME": "id", "TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True, - "USE_DEPRECATED_FILTERS": True, + "PAGINATION_DEFAULT_LIMIT": 100, } # @@ -816,6 +840,18 @@ for plugin_name in PLUGINS: f"__init__.py file and point to the PluginConfig subclass." ) + # Validate version compatibility and user-provided configuration settings and assign defaults + if plugin_name not in PLUGINS_CONFIG: + PLUGINS_CONFIG[plugin_name] = {} + try: + plugin_config.validate(PLUGINS_CONFIG[plugin_name], RELEASE.version) + except IncompatiblePluginError as e: + warnings.warn(f'Unable to load plugin {plugin_name}: {e}') + continue + + # Register the plugin as installed successfully + registry['plugins']['installed'].append(plugin_name) + plugin_module = "{}.{}".format(plugin_config.__module__, plugin_config.__name__) # type: ignore # Gather additional apps to load alongside this plugin @@ -845,11 +881,6 @@ for plugin_name in PLUGINS: sorted_apps = reversed(list(dict.fromkeys(reversed(INSTALLED_APPS)))) INSTALLED_APPS = list(sorted_apps) - # Validate user-provided configuration settings and assign defaults - if plugin_name not in PLUGINS_CONFIG: - PLUGINS_CONFIG[plugin_name] = {} - plugin_config.validate(PLUGINS_CONFIG[plugin_name], RELEASE.version) - # Add middleware plugin_middleware = plugin_config.middleware if plugin_middleware and type(plugin_middleware) in (list, tuple): @@ -871,6 +902,7 @@ for plugin_name in PLUGINS: else: raise ImproperlyConfigured(f"events_pipline in plugin: {plugin_name} must be a list or tuple") + # UNSUPPORTED FUNCTIONALITY: Import any local overrides. try: from .local_settings import * diff --git a/netbox/netbox/staging.py b/netbox/netbox/staging.py deleted file mode 100644 index e6b946403..000000000 --- a/netbox/netbox/staging.py +++ /dev/null @@ -1,148 +0,0 @@ -import logging - -from django.contrib.contenttypes.models import ContentType -from django.db import transaction -from django.db.models.signals import m2m_changed, pre_delete, post_save - -from extras.choices import ChangeActionChoices -from extras.models import StagedChange -from utilities.serialization import serialize_object - -logger = logging.getLogger('netbox.staging') - - -class checkout: - """ - Context manager for staging changes to NetBox objects. Staged changes are saved out-of-band - (as Change instances) for application at a later time, without modifying the production - database. - - branch = Branch.objects.create(name='my-branch') - with checkout(branch): - # All changes made herein will be rolled back and stored for later - - Note that invoking the context disabled transaction autocommit to facilitate manual rollbacks, - and restores its original value upon exit. - """ - def __init__(self, branch): - self.branch = branch - self.queue = {} - - def __enter__(self): - - # Disable autocommit to effect a new transaction - logger.debug(f"Entering transaction for {self.branch}") - self._autocommit = transaction.get_autocommit() - transaction.set_autocommit(False) - - # Apply any existing Changes assigned to this Branch - staged_changes = self.branch.staged_changes.all() - if change_count := staged_changes.count(): - logger.debug(f"Applying {change_count} pre-staged changes...") - for change in staged_changes: - change.apply() - else: - logger.debug("No pre-staged changes found") - - # Connect signal handlers - logger.debug("Connecting signal handlers") - post_save.connect(self.post_save_handler) - m2m_changed.connect(self.post_save_handler) - pre_delete.connect(self.pre_delete_handler) - - def __exit__(self, exc_type, exc_val, exc_tb): - - # Disconnect signal handlers - logger.debug("Disconnecting signal handlers") - post_save.disconnect(self.post_save_handler) - m2m_changed.disconnect(self.post_save_handler) - pre_delete.disconnect(self.pre_delete_handler) - - # Roll back the transaction to return the database to its original state - logger.debug("Rolling back database transaction") - transaction.rollback() - logger.debug(f"Restoring autocommit state ({self._autocommit})") - transaction.set_autocommit(self._autocommit) - - # Process queued changes - self.process_queue() - - # - # Queuing - # - - @staticmethod - def get_key_for_instance(instance): - return ContentType.objects.get_for_model(instance), instance.pk - - def process_queue(self): - """ - Create Change instances for all actions stored in the queue. - """ - if not self.queue: - logger.debug("No queued changes; aborting") - return - logger.debug(f"Processing {len(self.queue)} queued changes") - - # Iterate through the in-memory queue, creating Change instances - changes = [] - for key, change in self.queue.items(): - logger.debug(f' {key}: {change}') - object_type, pk = key - action, data = change - - changes.append(StagedChange( - branch=self.branch, - action=action, - object_type=object_type, - object_id=pk, - data=data - )) - - # Save all Change instances to the database - StagedChange.objects.bulk_create(changes) - - # - # Signal handlers - # - - def post_save_handler(self, sender, instance, **kwargs): - """ - Hooks to the post_save signal when a branch is active to queue create and update actions. - """ - key = self.get_key_for_instance(instance) - object_type = instance._meta.verbose_name - - # Creating a new object - if kwargs.get('created'): - logger.debug(f"[{self.branch}] Staging creation of {object_type} {instance} (PK: {instance.pk})") - data = serialize_object(instance, resolve_tags=False) - self.queue[key] = (ChangeActionChoices.ACTION_CREATE, data) - return - - # Ignore pre_* many-to-many actions - if 'action' in kwargs and kwargs['action'] not in ('post_add', 'post_remove', 'post_clear'): - return - - # Object has already been created/updated in the queue; update its queued representation - if key in self.queue: - logger.debug(f"[{self.branch}] Updating staged value for {object_type} {instance} (PK: {instance.pk})") - data = serialize_object(instance, resolve_tags=False) - self.queue[key] = (self.queue[key][0], data) - return - - # Modifying an existing object for the first time - logger.debug(f"[{self.branch}] Staging changes to {object_type} {instance} (PK: {instance.pk})") - data = serialize_object(instance, resolve_tags=False) - self.queue[key] = (ChangeActionChoices.ACTION_UPDATE, data) - - def pre_delete_handler(self, sender, instance, **kwargs): - """ - Hooks to the pre_delete signal when a branch is active to queue delete actions. - """ - key = self.get_key_for_instance(instance) - object_type = instance._meta.verbose_name - - # Delete an existing object - logger.debug(f"[{self.branch}] Staging deletion of {object_type} {instance} (PK: {instance.pk})") - self.queue[key] = (ChangeActionChoices.ACTION_DELETE, None) diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index cf6e1f133..3d36ec8f8 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -35,6 +35,7 @@ __all__ = ( 'ContentTypesColumn', 'CustomFieldColumn', 'CustomLinkColumn', + 'DictColumn', 'DistanceColumn', 'DurationColumn', 'LinkedCountColumn', @@ -259,11 +260,15 @@ class ActionsColumn(tables.Column): return '' def render(self, record, table, **kwargs): - # Skip dummy records (e.g. available VLANs) or those with no actions - if not getattr(record, 'pk', None) or not (self.actions or self.extra_buttons): + model = table.Meta.model + + # Skip if no actions or extra buttons are defined + if not (self.actions or self.extra_buttons): + return '' + # Skip dummy records (e.g. available VLANs or IP ranges replacing individual IPs) + if type(record) is not model or not getattr(record, 'pk', None): return '' - model = table.Meta.model if request := getattr(table, 'context', {}).get('request'): return_url = request.GET.get('return_url', request.get_full_path()) url_appendix = f'?return_url={quote(return_url)}' @@ -707,3 +712,14 @@ class DistanceColumn(TemplateColumn): def __init__(self, template_code=template_code, order_by='_abs_distance', **kwargs): super().__init__(template_code=template_code, order_by=order_by, **kwargs) + + +class DictColumn(tables.Column): + """ + Render a dictionary of data in a simple key: value format, one pair per line. + """ + def render(self, value): + output = '
'.join([ + f'{escape(k)}: {escape(v)}' for k, v in value.items() + ]) + return mark_safe(output) diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index 2d2c430aa..6f6b30af2 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -1,9 +1,9 @@ from copy import deepcopy from functools import cached_property +from urllib.parse import urlencode import django_tables2 as tables from django.conf import settings -from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import FieldDoesNotExist from django.db.models.fields.related import RelatedField @@ -20,8 +20,8 @@ from extras.models import CustomField, CustomLink from netbox.constants import EMPTY_TABLE_TEXT from netbox.registry import registry from netbox.tables import columns -from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.html import highlight +from utilities.paginator import EnhancedPaginator, get_paginate_count from utilities.string import title from utilities.views import get_viewname from .template_code import * @@ -58,40 +58,6 @@ class BaseTable(tables.Table): if self.empty_text is None: self.empty_text = _("No {model_name} found").format(model_name=self._meta.model._meta.verbose_name_plural) - # Determine the table columns to display by checking the following: - # 1. User's configuration for the table - # 2. Meta.default_columns - # 3. Meta.fields - selected_columns = None - if user is not None and not isinstance(user, AnonymousUser): - selected_columns = user.config.get(f"tables.{self.name}.columns") - elif isinstance(user, AnonymousUser) and hasattr(settings, 'DEFAULT_USER_PREFERENCES'): - selected_columns = settings.DEFAULT_USER_PREFERENCES.get('tables', {}).get(self.name, {}).get('columns') - if not selected_columns: - selected_columns = getattr(self.Meta, 'default_columns', self.Meta.fields) - - # Hide non-selected columns which are not exempt - for column in self.columns: - if column.name not in [*selected_columns, *self.exempt_columns]: - self.columns.hide(column.name) - - # Rearrange the sequence to list selected columns first, followed by all remaining columns - # TODO: There's probably a more clever way to accomplish this - self.sequence = [ - *[c for c in selected_columns if c in self.columns.names()], - *[c for c in self.columns.names() if c not in selected_columns] - ] - - # PK column should always come first - if 'pk' in self.sequence: - self.sequence.remove('pk') - self.sequence.insert(0, 'pk') - - # Actions column should always come last - if 'actions' in self.sequence: - self.sequence.remove('actions') - self.sequence.append('actions') - # Dynamically update the table's QuerySet to ensure related fields are pre-fetched if isinstance(self.data, TableQuerysetData): @@ -147,25 +113,66 @@ class BaseTable(tables.Table): self._objects_count = sum(1 for obj in self.data if hasattr(obj, 'pk')) return self._objects_count + def _set_columns(self, selected_columns): + """ + Update the table sequence to display only the named columns and any exempt columns. + """ + # Hide non-selected columns which are not exempt + for column in self.columns: + if column.name not in [*selected_columns, *self.exempt_columns]: + self.columns.hide(column.name) + + # Rearrange the sequence to list selected columns first, followed by all remaining columns + # TODO: There's probably a more clever way to accomplish this + self.sequence = [ + *[c for c in selected_columns if c in self.columns.names()], + *[c for c in self.columns.names() if c not in selected_columns] + ] + + # PK column should always come first + if 'pk' in self.sequence: + self.sequence.remove('pk') + self.sequence.insert(0, 'pk') + + # Actions column should always come last + if 'actions' in self.sequence: + self.sequence.remove('actions') + self.sequence.append('actions') + def configure(self, request): """ Configure the table for a specific request context. This performs pagination and records - the user's preferred ordering logic. + the user's preferred columns & ordering logic. """ - # Save ordering preference - if request.user.is_authenticated: - if self.prefixed_order_by_field in request.GET: - if request.GET[self.prefixed_order_by_field]: - # If an ordering has been specified as a query parameter, save it as the - # user's preferred ordering for this table. - ordering = request.GET.getlist(self.prefixed_order_by_field) - request.user.config.set(f'tables.{self.name}.ordering', ordering, commit=True) - else: - # If the ordering has been set to none (empty), clear any existing preference. - request.user.config.clear(f'tables.{self.name}.ordering', commit=True) - elif ordering := request.user.config.get(f'tables.{self.name}.ordering'): - # If no ordering has been specified, set the preferred ordering (if any). - self.order_by = ordering + columns = None + ordering = None + + if self.prefixed_order_by_field in request.GET: + if request.GET[self.prefixed_order_by_field]: + # If an ordering has been specified as a query parameter, save it as the + # user's preferred ordering for this table. + ordering = request.GET.getlist(self.prefixed_order_by_field) + request.user.config.set(f'tables.{self.name}.ordering', ordering, commit=True) + else: + # If the ordering has been set to none (empty), clear any existing preference. + request.user.config.clear(f'tables.{self.name}.ordering', commit=True) + + # If the user has a saved preference, apply it + if request.user.is_authenticated and (userconfig := request.user.config): + if columns is None: + columns = userconfig.get(f"tables.{self.name}.columns") + if ordering is None: + ordering = userconfig.get(f"tables.{self.name}.ordering") + + # Fall back to the default columns & ordering + if columns is None and hasattr(settings, 'DEFAULT_USER_PREFERENCES'): + columns = settings.DEFAULT_USER_PREFERENCES.get('tables', {}).get(self.name, {}).get('columns') + if columns is None: + columns = getattr(self.Meta, 'default_columns', self.Meta.fields) + + self._set_columns(columns) + if ordering is not None: + self.order_by = ordering # Paginate the table results paginate = { @@ -174,6 +181,25 @@ class BaseTable(tables.Table): } tables.RequestConfig(request, paginate).configure(self) + @property + def configuration(self): + config = { + 'columns': ','.join([c[0] for c in self.selected_columns]), + } + if self.order_by: + config['ordering'] = self.order_by + return config + + @property + def config_params(self): + if not (model := getattr(self.Meta, 'model', None)): + return None + return urlencode({ + 'object_type': ObjectType.objects.get_for_model(model).pk, + 'table': self.name, + **self.configuration, + }) + class NetBoxTable(BaseTable): """ diff --git a/netbox/netbox/tests/dummy_plugin/template_content.py b/netbox/netbox/tests/dummy_plugin/template_content.py index e9a6b9da1..e962594d4 100644 --- a/netbox/netbox/tests/dummy_plugin/template_content.py +++ b/netbox/netbox/tests/dummy_plugin/template_content.py @@ -3,6 +3,9 @@ from netbox.plugins.templates import PluginTemplateExtension class GlobalContent(PluginTemplateExtension): + def head(self): + return "" + def navbar(self): return "GLOBAL CONTENT - NAVBAR" diff --git a/netbox/netbox/tests/test_graphql.py b/netbox/netbox/tests/test_graphql.py index b04d42d24..ca231526f 100644 --- a/netbox/netbox/tests/test_graphql.py +++ b/netbox/netbox/tests/test_graphql.py @@ -99,8 +99,8 @@ class GraphQLAPITestCase(APITestCase): # Test OR logic query = """{ location_list( filters: { - status: \"""" + LocationStatusChoices.STATUS_PLANNED + """\", - OR: {status: \"""" + LocationStatusChoices.STATUS_STAGING + """\"} + status: STATUS_PLANNED, + OR: {status: STATUS_STAGING} }) { id site {id} } diff --git a/netbox/netbox/tests/test_staging.py b/netbox/netbox/tests/test_staging.py deleted file mode 100644 index 0a73b2987..000000000 --- a/netbox/netbox/tests/test_staging.py +++ /dev/null @@ -1,216 +0,0 @@ -from django.db.models.signals import post_save -from django.test import TransactionTestCase - -from circuits.models import Provider, Circuit, CircuitType -from extras.choices import ChangeActionChoices -from extras.models import Branch, StagedChange, Tag -from ipam.models import ASN, RIR -from netbox.search.backends import search_backend -from netbox.staging import checkout -from utilities.testing import create_tags - - -class StagingTestCase(TransactionTestCase): - - def setUp(self): - # Disconnect search backend to avoid issues with cached ObjectTypes being deleted - # from the database upon transaction rollback - post_save.disconnect(search_backend.caching_handler) - - create_tags('Alpha', 'Bravo', 'Charlie') - - rir = RIR.objects.create(name='RIR 1', slug='rir-1') - asns = ( - ASN(asn=65001, rir=rir), - ASN(asn=65002, rir=rir), - ASN(asn=65003, rir=rir), - ) - ASN.objects.bulk_create(asns) - - providers = ( - Provider(name='Provider A', slug='provider-a'), - Provider(name='Provider B', slug='provider-b'), - Provider(name='Provider C', slug='provider-c'), - ) - Provider.objects.bulk_create(providers) - - circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1') - - Circuit.objects.bulk_create(( - Circuit(provider=providers[0], cid='Circuit A1', type=circuit_type), - Circuit(provider=providers[0], cid='Circuit A2', type=circuit_type), - Circuit(provider=providers[0], cid='Circuit A3', type=circuit_type), - Circuit(provider=providers[1], cid='Circuit B1', type=circuit_type), - Circuit(provider=providers[1], cid='Circuit B2', type=circuit_type), - Circuit(provider=providers[1], cid='Circuit B3', type=circuit_type), - Circuit(provider=providers[2], cid='Circuit C1', type=circuit_type), - Circuit(provider=providers[2], cid='Circuit C2', type=circuit_type), - Circuit(provider=providers[2], cid='Circuit C3', type=circuit_type), - )) - - def test_object_creation(self): - branch = Branch.objects.create(name='Branch 1') - tags = Tag.objects.all() - asns = ASN.objects.all() - - with checkout(branch): - provider = Provider.objects.create(name='Provider D', slug='provider-d') - provider.asns.set(asns) - circuit = Circuit.objects.create(provider=provider, cid='Circuit D1', type=CircuitType.objects.first()) - circuit.tags.set(tags) - - # Sanity-checking - self.assertEqual(Provider.objects.count(), 4) - self.assertListEqual(list(provider.asns.all()), list(asns)) - self.assertEqual(Circuit.objects.count(), 10) - self.assertListEqual(list(circuit.tags.all()), list(tags)) - - # Verify that changes have been rolled back after exiting the context - self.assertEqual(Provider.objects.count(), 3) - self.assertEqual(Circuit.objects.count(), 9) - self.assertEqual(StagedChange.objects.count(), 5) - - # Verify that changes are replayed upon entering the context - with checkout(branch): - self.assertEqual(Provider.objects.count(), 4) - self.assertEqual(Circuit.objects.count(), 10) - provider = Provider.objects.get(name='Provider D') - self.assertListEqual(list(provider.asns.all()), list(asns)) - circuit = Circuit.objects.get(cid='Circuit D1') - self.assertListEqual(list(circuit.tags.all()), list(tags)) - - # Verify that changes are applied and deleted upon branch merge - branch.merge() - self.assertEqual(Provider.objects.count(), 4) - self.assertEqual(Circuit.objects.count(), 10) - provider = Provider.objects.get(name='Provider D') - self.assertListEqual(list(provider.asns.all()), list(asns)) - circuit = Circuit.objects.get(cid='Circuit D1') - self.assertListEqual(list(circuit.tags.all()), list(tags)) - self.assertEqual(StagedChange.objects.count(), 0) - - def test_object_modification(self): - branch = Branch.objects.create(name='Branch 1') - tags = Tag.objects.all() - asns = ASN.objects.all() - - with checkout(branch): - provider = Provider.objects.get(name='Provider A') - provider.name = 'Provider X' - provider.save() - provider.asns.set(asns) - circuit = Circuit.objects.get(cid='Circuit A1') - circuit.cid = 'Circuit X' - circuit.save() - circuit.tags.set(tags) - - # Sanity-checking - self.assertEqual(Provider.objects.count(), 3) - self.assertEqual(Provider.objects.get(pk=provider.pk).name, 'Provider X') - self.assertListEqual(list(provider.asns.all()), list(asns)) - self.assertEqual(Circuit.objects.count(), 9) - self.assertEqual(Circuit.objects.get(pk=circuit.pk).cid, 'Circuit X') - self.assertListEqual(list(circuit.tags.all()), list(tags)) - - # Verify that changes have been rolled back after exiting the context - self.assertEqual(Provider.objects.count(), 3) - self.assertEqual(Provider.objects.get(pk=provider.pk).name, 'Provider A') - provider = Provider.objects.get(pk=provider.pk) - self.assertListEqual(list(provider.asns.all()), []) - self.assertEqual(Circuit.objects.count(), 9) - circuit = Circuit.objects.get(pk=circuit.pk) - self.assertEqual(circuit.cid, 'Circuit A1') - self.assertListEqual(list(circuit.tags.all()), []) - self.assertEqual(StagedChange.objects.count(), 5) - - # Verify that changes are replayed upon entering the context - with checkout(branch): - self.assertEqual(Provider.objects.count(), 3) - self.assertEqual(Provider.objects.get(pk=provider.pk).name, 'Provider X') - provider = Provider.objects.get(pk=provider.pk) - self.assertListEqual(list(provider.asns.all()), list(asns)) - self.assertEqual(Circuit.objects.count(), 9) - circuit = Circuit.objects.get(pk=circuit.pk) - self.assertEqual(circuit.cid, 'Circuit X') - self.assertListEqual(list(circuit.tags.all()), list(tags)) - - # Verify that changes are applied and deleted upon branch merge - branch.merge() - self.assertEqual(Provider.objects.count(), 3) - self.assertEqual(Provider.objects.get(pk=provider.pk).name, 'Provider X') - provider = Provider.objects.get(pk=provider.pk) - self.assertListEqual(list(provider.asns.all()), list(asns)) - self.assertEqual(Circuit.objects.count(), 9) - circuit = Circuit.objects.get(pk=circuit.pk) - self.assertEqual(circuit.cid, 'Circuit X') - self.assertListEqual(list(circuit.tags.all()), list(tags)) - self.assertEqual(StagedChange.objects.count(), 0) - - def test_object_deletion(self): - branch = Branch.objects.create(name='Branch 1') - - with checkout(branch): - provider = Provider.objects.get(name='Provider A') - provider.circuits.all().delete() - provider.delete() - - # Sanity-checking - self.assertEqual(Provider.objects.count(), 2) - self.assertEqual(Circuit.objects.count(), 6) - - # Verify that changes have been rolled back after exiting the context - self.assertEqual(Provider.objects.count(), 3) - self.assertEqual(Circuit.objects.count(), 9) - self.assertEqual(StagedChange.objects.count(), 4) - - # Verify that changes are replayed upon entering the context - with checkout(branch): - self.assertEqual(Provider.objects.count(), 2) - self.assertEqual(Circuit.objects.count(), 6) - - # Verify that changes are applied and deleted upon branch merge - branch.merge() - self.assertEqual(Provider.objects.count(), 2) - self.assertEqual(Circuit.objects.count(), 6) - self.assertEqual(StagedChange.objects.count(), 0) - - def test_exit_enter_context(self): - branch = Branch.objects.create(name='Branch 1') - - with checkout(branch): - - # Create a new object - provider = Provider.objects.create(name='Provider D', slug='provider-d') - provider.save() - - # Check that a create Change was recorded - self.assertEqual(StagedChange.objects.count(), 1) - change = StagedChange.objects.first() - self.assertEqual(change.action, ChangeActionChoices.ACTION_CREATE) - self.assertEqual(change.data['name'], provider.name) - - with checkout(branch): - - # Update the staged object - provider = Provider.objects.get(name='Provider D') - provider.comments = 'New comments' - provider.save() - - # Check that a second Change object has been created for the object - self.assertEqual(StagedChange.objects.count(), 2) - change = StagedChange.objects.last() - self.assertEqual(change.action, ChangeActionChoices.ACTION_UPDATE) - self.assertEqual(change.data['name'], provider.name) - self.assertEqual(change.data['comments'], provider.comments) - - with checkout(branch): - - # Delete the staged object - provider = Provider.objects.get(name='Provider D') - provider.delete() - - # Check that a third Change has recorded the object's deletion - self.assertEqual(StagedChange.objects.count(), 3) - change = StagedChange.objects.last() - self.assertEqual(change.action, ChangeActionChoices.ACTION_DELETE) - self.assertIsNone(change.data) diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 373864695..4fd23e84c 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -30,6 +30,7 @@ from utilities.htmx import htmx_partial from utilities.permissions import get_permission_for_model from utilities.query import reapply_model_ordering from utilities.request import safe_for_redirect +from utilities.tables import get_table_configs from utilities.views import GetReturnURLMixin, get_viewname from .base import BaseMultiObjectView from .mixins import ActionsMixin, TableMixin @@ -109,7 +110,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): request: The current request """ try: - return template.render_to_response(self.queryset) + return template.render_to_response(queryset=self.queryset) except Exception as e: messages.error( request, @@ -195,6 +196,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): context = { 'model': model, 'table': table, + 'table_configs': get_table_configs(table, request.user), 'actions': actions, 'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, 'prerequisite_model': get_prerequisite_model(self.queryset), diff --git a/netbox/netbox/views/generic/feature_views.py b/netbox/netbox/views/generic/feature_views.py index 01c4b2862..9ad14a3d0 100644 --- a/netbox/netbox/views/generic/feature_views.py +++ b/netbox/netbox/views/generic/feature_views.py @@ -12,13 +12,19 @@ from core.tables import JobTable, ObjectChangeTable from extras.forms import JournalEntryForm from extras.models import JournalEntry from extras.tables import JournalEntryTable +from tenancy.models import ContactAssignment +from tenancy.tables import ContactAssignmentTable +from tenancy.filtersets import ContactAssignmentFilterSet +from tenancy.forms import ContactAssignmentFilterForm from utilities.permissions import get_permission_for_model from utilities.views import ConditionalLoginRequiredMixin, GetReturnURLMixin, ViewTab from .base import BaseMultiObjectView +from .object_views import ObjectChildrenView __all__ = ( 'BulkSyncDataView', 'ObjectChangeLogView', + 'ObjectContactsView', 'ObjectJobsView', 'ObjectJournalView', 'ObjectSyncDataView', @@ -244,3 +250,25 @@ class BulkSyncDataView(GetReturnURLMixin, BaseMultiObjectView): )) return redirect(self.get_return_url(request)) + + +class ObjectContactsView(ObjectChildrenView): + child_model = ContactAssignment + table = ContactAssignmentTable + filterset = ContactAssignmentFilterSet + filterset_form = ContactAssignmentFilterForm + template_name = 'tenancy/object_contacts.html' + tab = ViewTab( + label=_('Contacts'), + badge=lambda obj: obj.get_contacts().count(), + permission='tenancy.view_contactassignment', + weight=5000 + ) + + def dispatch(self, request, *args, **kwargs): + model = kwargs.pop('model') + self.queryset = model.objects.all() + return super().dispatch(request, *args, **kwargs) + + def get_children(self, request, parent): + return parent.get_contacts().restrict(request.user, 'view').order_by('priority', 'contact', 'role') diff --git a/netbox/netbox/views/generic/mixins.py b/netbox/netbox/views/generic/mixins.py index 25621b7bd..5f9f62120 100644 --- a/netbox/netbox/views/generic/mixins.py +++ b/netbox/netbox/views/generic/mixins.py @@ -1,3 +1,6 @@ +from django.shortcuts import get_object_or_404 + +from extras.models import TableConfig from netbox.constants import DEFAULT_ACTION_PERMISSIONS from utilities.permissions import get_permission_for_model @@ -47,6 +50,15 @@ class TableMixin: request: The current request bulk_actions: Render checkboxes for object selection """ + + # If a TableConfig has been specified, apply it & update the user's saved preference + if tableconfig_id := request.GET.get('tableconfig_id'): + tableconfig = get_object_or_404(TableConfig, pk=tableconfig_id) + if request.user.is_authenticated: + table = self.table.__name__ + request.user.config.set(f'tables.{table}.columns', tableconfig.columns) + request.user.config.set(f'tables.{table}.ordering', tableconfig.ordering, commit=True) + table = self.table(data, user=request.user) if 'pk' in table.base_columns and bulk_actions: table.columns.show('pk') diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 36d711439..0db73b7a6 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -21,6 +21,7 @@ from utilities.htmx import htmx_partial from utilities.permissions import get_permission_for_model from utilities.querydict import normalize_querydict, prepare_cloned_fields from utilities.request import safe_for_redirect +from utilities.tables import get_table_configs from utilities.views import GetReturnURLMixin, get_viewname from .base import BaseObjectView from .mixins import ActionsMixin, TableMixin @@ -157,6 +158,7 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin): 'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html', 'table': table, 'table_config': f'{table.name}_config', + 'table_configs': get_table_configs(table, request.user), 'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, 'actions': actions, 'tab': self.tab, diff --git a/netbox/project-static/dist/netbox-external.css b/netbox/project-static/dist/netbox-external.css index 702520216..a7390f98f 100644 Binary files a/netbox/project-static/dist/netbox-external.css and b/netbox/project-static/dist/netbox-external.css differ diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index 3be9ba632..26d652ad0 100644 Binary files a/netbox/project-static/dist/netbox.css and b/netbox/project-static/dist/netbox.css differ diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 814f9f568..8f86f8ac7 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 88c6c8050..22171ec2c 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/netbox-graphiql/package.json b/netbox/project-static/netbox-graphiql/package.json index 00218185a..be4f8911f 100644 --- a/netbox/project-static/netbox-graphiql/package.json +++ b/netbox/project-static/netbox-graphiql/package.json @@ -1,6 +1,6 @@ { "name": "netbox-graphiql", - "version": "4.2.0", + "version": "4.3.0", "description": "NetBox GraphiQL Custom Front End", "main": "dist/graphiql.js", "license": "Apache-2.0", diff --git a/netbox/project-static/package.json b/netbox/project-static/package.json index 935c82d90..9bef4ac73 100644 --- a/netbox/project-static/package.json +++ b/netbox/project-static/package.json @@ -1,6 +1,6 @@ { "name": "netbox", - "version": "4.2.8", + "version": "4.3.0", "main": "dist/netbox.js", "license": "Apache-2.0", "private": true, @@ -23,12 +23,12 @@ }, "dependencies": { "@mdi/font": "7.4.47", - "@tabler/core": "1.0.0-beta21", + "@tabler/core": "1.2.0", "bootstrap": "5.3.5", "clipboard": "2.0.11", "flatpickr": "4.6.13", - "gridstack": "11.5.0", - "htmx.org": "1.9.12", + "gridstack": "12.1.1", + "htmx.org": "2.0.4", "query-string": "9.1.1", "sass": "1.87.0", "tom-select": "2.4.3", @@ -41,7 +41,7 @@ "@types/node": "^22.3.0", "@typescript-eslint/eslint-plugin": "^8.1.0", "@typescript-eslint/parser": "^8.1.0", - "esbuild": "^0.24.2", + "esbuild": "^0.25.3", "esbuild-sass-plugin": "^3.3.1", "eslint": "<9.0", "eslint-config-prettier": "^9.1.0", diff --git a/netbox/project-static/src/forms/elements.ts b/netbox/project-static/src/forms/elements.ts index e047ea738..a397feed6 100644 --- a/netbox/project-static/src/forms/elements.ts +++ b/netbox/project-static/src/forms/elements.ts @@ -1,6 +1,12 @@ import { getElements, scrollTo } from '../util'; function handleFormSubmit(event: Event, form: HTMLFormElement): void { + // Automatically select all options in any - {% elif 'next' in request.POST %} - - {% endif %} + {# Set post-login URL #} + {% if 'next' in request.GET %} + + {% elif 'next' in request.POST %} + + {% endif %} -
- - {{ form.username }} - {% for error in form.username.errors %} -
{{ error }}
- {% endfor %} -
+
+ + {{ form.username }} + {% for error in form.username.errors %} +
{{ error }}
+ {% endfor %} +
-
- - {{ form.password }} - {% for error in form.password.errors %} -
{{ error }}
- {% endfor %} -
+
+ + {{ form.password }} + {% for error in form.password.errors %} +
{{ error }}
+ {% endfor %} +
- - - + + + + {% endif %} {# SSO login #} {% if auth_backends %} -
{% trans "Or" context "Denotes an alternative option" %}
+ {% if not login_form_hidden %} +
{% trans "Or" context "Denotes an alternative option" %}
+ {% endif %}
+ {% if login_form_hidden %} +

{% trans "Log In" %}

+ {% endif %}
{% for backend in auth_backends %}
diff --git a/netbox/templates/tenancy/contact.html b/netbox/templates/tenancy/contact.html index 48a823a06..790e08489 100644 --- a/netbox/templates/tenancy/contact.html +++ b/netbox/templates/tenancy/contact.html @@ -18,8 +18,18 @@

{% trans "Contact" %}

- - + + diff --git a/netbox/templates/tenancy/contactgroup.html b/netbox/templates/tenancy/contactgroup.html index 7e34cf122..bdcf675dd 100644 --- a/netbox/templates/tenancy/contactgroup.html +++ b/netbox/templates/tenancy/contactgroup.html @@ -32,6 +32,7 @@
{% trans "Group" %}{{ object.group|linkify|placeholder }}{% trans "Groups" %} + {% if object.groups.all|length > 0 %} +
    + {% for group in object.groups.all %} +
  1. {{ group|linkify|placeholder }}
  2. + {% endfor %} +
+ {% else %} + {{ ''|placeholder }} + {% endif %} +
{% trans "Name" %}
{% include 'inc/panels/tags.html' %} + {% include 'inc/panels/comments.html' %} {% plugin_left_page object %}
diff --git a/netbox/templates/tenancy/tenantgroup.html b/netbox/templates/tenancy/tenantgroup.html index bf8cb3204..5ca3ba554 100644 --- a/netbox/templates/tenancy/tenantgroup.html +++ b/netbox/templates/tenancy/tenantgroup.html @@ -40,6 +40,7 @@
{% include 'inc/panels/tags.html' %} + {% include 'inc/panels/comments.html' %} {% plugin_left_page object %}
diff --git a/netbox/templates/vpn/l2vpn.html b/netbox/templates/vpn/l2vpn.html index fd47da3e6..593123849 100644 --- a/netbox/templates/vpn/l2vpn.html +++ b/netbox/templates/vpn/l2vpn.html @@ -22,6 +22,10 @@ {% trans "Type" %} {{ object.get_type_display }} + + {% trans "Status" %} + {% badge object.get_status_display bg_color=object.get_status_color %} + {% trans "Description" %} {{ object.description|placeholder }} diff --git a/netbox/templates/wireless/wirelesslangroup.html b/netbox/templates/wireless/wirelesslangroup.html index ee6cafaa3..5440ffe4f 100644 --- a/netbox/templates/wireless/wirelesslangroup.html +++ b/netbox/templates/wireless/wirelesslangroup.html @@ -40,6 +40,7 @@
{% include 'inc/panels/tags.html' %} + {% include 'inc/panels/comments.html' %} {% plugin_left_page object %}
diff --git a/netbox/tenancy/api/serializers_/contacts.py b/netbox/tenancy/api/serializers_/contacts.py index 8c24df734..fd4d1ac8e 100644 --- a/netbox/tenancy/api/serializers_/contacts.py +++ b/netbox/tenancy/api/serializers_/contacts.py @@ -3,7 +3,7 @@ from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field from rest_framework import serializers -from netbox.api.fields import ChoiceField, ContentTypeField +from netbox.api.fields import ChoiceField, ContentTypeField, SerializedPKRelatedField from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer from tenancy.choices import ContactPriorityChoices from tenancy.models import ContactAssignment, Contact, ContactGroup, ContactRole @@ -26,7 +26,7 @@ class ContactGroupSerializer(NestedGroupModelSerializer): model = ContactGroup fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', - 'created', 'last_updated', 'contact_count', '_depth', + 'created', 'last_updated', 'contact_count', 'comments', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'contact_count', '_depth') @@ -43,12 +43,18 @@ class ContactRoleSerializer(NetBoxModelSerializer): class ContactSerializer(NetBoxModelSerializer): - group = ContactGroupSerializer(nested=True, required=False, allow_null=True, default=None) + groups = SerializedPKRelatedField( + queryset=ContactGroup.objects.all(), + serializer=ContactGroupSerializer, + required=False, + many=True, + nested=True + ) class Meta: model = Contact fields = [ - 'id', 'url', 'display_url', 'display', 'group', 'name', 'title', 'phone', 'email', 'address', 'link', + 'id', 'url', 'display_url', 'display', 'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/tenancy/api/serializers_/tenants.py b/netbox/tenancy/api/serializers_/tenants.py index 54e906f1d..189397c70 100644 --- a/netbox/tenancy/api/serializers_/tenants.py +++ b/netbox/tenancy/api/serializers_/tenants.py @@ -19,7 +19,7 @@ class TenantGroupSerializer(NestedGroupModelSerializer): model = TenantGroup fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', - 'created', 'last_updated', 'tenant_count', '_depth', + 'created', 'last_updated', 'tenant_count', 'comments', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tenant_count', '_depth') diff --git a/netbox/tenancy/api/views.py b/netbox/tenancy/api/views.py index 70330ddb8..371b8ec24 100644 --- a/netbox/tenancy/api/views.py +++ b/netbox/tenancy/api/views.py @@ -44,7 +44,7 @@ class ContactGroupViewSet(MPTTLockedMixin, NetBoxModelViewSet): queryset = ContactGroup.objects.add_related_count( ContactGroup.objects.all(), Contact, - 'group', + 'groups', 'contact_count', cumulative=True ) diff --git a/netbox/tenancy/filtersets.py b/netbox/tenancy/filtersets.py index e2de18231..ca0142db6 100644 --- a/netbox/tenancy/filtersets.py +++ b/netbox/tenancy/filtersets.py @@ -2,7 +2,7 @@ import django_filters from django.db.models import Q from django.utils.translation import gettext as _ -from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet +from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter from .models import * @@ -22,7 +22,7 @@ __all__ = ( # Contacts # -class ContactGroupFilterSet(OrganizationalModelFilterSet): +class ContactGroupFilterSet(NestedGroupModelFilterSet): parent_id = django_filters.ModelMultipleChoiceFilter( queryset=ContactGroup.objects.all(), label=_('Parent contact group (ID)'), @@ -46,6 +46,11 @@ class ContactGroupFilterSet(OrganizationalModelFilterSet): to_field_name='slug', label=_('Contact group (slug)'), ) + contact_id = django_filters.ModelMultipleChoiceFilter( + field_name='contact', + queryset=Contact.objects.all(), + label=_('Contact (ID)'), + ) class Meta: model = ContactGroup @@ -62,15 +67,15 @@ class ContactRoleFilterSet(OrganizationalModelFilterSet): class ContactFilterSet(NetBoxModelFilterSet): group_id = TreeNodeMultipleChoiceFilter( queryset=ContactGroup.objects.all(), - field_name='group', + field_name='groups', lookup_expr='in', label=_('Contact group (ID)'), ) group = TreeNodeMultipleChoiceFilter( queryset=ContactGroup.objects.all(), - field_name='group', - lookup_expr='in', + field_name='groups', to_field_name='slug', + lookup_expr='in', label=_('Contact group (slug)'), ) @@ -105,13 +110,13 @@ class ContactAssignmentFilterSet(NetBoxModelFilterSet): ) group_id = TreeNodeMultipleChoiceFilter( queryset=ContactGroup.objects.all(), - field_name='contact__group', + field_name='contact__groups', lookup_expr='in', label=_('Contact group (ID)'), ) group = TreeNodeMultipleChoiceFilter( queryset=ContactGroup.objects.all(), - field_name='contact__group', + field_name='contact__groups', lookup_expr='in', to_field_name='slug', label=_('Contact group (slug)'), @@ -153,7 +158,7 @@ class ContactModelFilterSet(django_filters.FilterSet): ) contact_group = TreeNodeMultipleChoiceFilter( queryset=ContactGroup.objects.all(), - field_name='contacts__contact__group', + field_name='contacts__contact__groups', lookup_expr='in', label=_('Contact group'), ) @@ -163,7 +168,7 @@ class ContactModelFilterSet(django_filters.FilterSet): # Tenancy # -class TenantGroupFilterSet(OrganizationalModelFilterSet): +class TenantGroupFilterSet(NestedGroupModelFilterSet): parent_id = django_filters.ModelMultipleChoiceFilter( queryset=TenantGroup.objects.all(), label=_('Parent tenant group (ID)'), diff --git a/netbox/tenancy/forms/bulk_edit.py b/netbox/tenancy/forms/bulk_edit.py index d3a63fdcd..9e6576076 100644 --- a/netbox/tenancy/forms/bulk_edit.py +++ b/netbox/tenancy/forms/bulk_edit.py @@ -5,7 +5,7 @@ from netbox.forms import NetBoxModelBulkEditForm from tenancy.choices import ContactPriorityChoices from tenancy.models import * from utilities.forms import add_blank_choice -from utilities.forms.fields import CommentField, DynamicModelChoiceField +from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField from utilities.forms.rendering import FieldSet __all__ = ( @@ -33,9 +33,10 @@ class TenantGroupBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = TenantGroup - nullable_fields = ('parent', 'description') + nullable_fields = ('parent', 'description', 'comments') class TenantBulkEditForm(NetBoxModelBulkEditForm): @@ -67,12 +68,13 @@ class ContactGroupBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = ContactGroup fieldsets = ( FieldSet('parent', 'description'), ) - nullable_fields = ('parent', 'description') + nullable_fields = ('parent', 'description', 'comments') class ContactRoleBulkEditForm(NetBoxModelBulkEditForm): @@ -90,8 +92,13 @@ class ContactRoleBulkEditForm(NetBoxModelBulkEditForm): class ContactBulkEditForm(NetBoxModelBulkEditForm): - group = DynamicModelChoiceField( - label=_('Group'), + add_groups = DynamicModelMultipleChoiceField( + label=_('Add groups'), + queryset=ContactGroup.objects.all(), + required=False + ) + remove_groups = DynamicModelMultipleChoiceField( + label=_('Remove groups'), queryset=ContactGroup.objects.all(), required=False ) @@ -128,9 +135,13 @@ class ContactBulkEditForm(NetBoxModelBulkEditForm): model = Contact fieldsets = ( - FieldSet('group', 'title', 'phone', 'email', 'address', 'link', 'description'), + FieldSet('title', 'phone', 'email', 'address', 'link', 'description'), + FieldSet('add_groups', 'remove_groups', name=_('Groups')), + ) + + nullable_fields = ( + 'add_groups', 'remove_groups', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments' ) - nullable_fields = ('group', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments') class ContactAssignmentBulkEditForm(NetBoxModelBulkEditForm): diff --git a/netbox/tenancy/forms/bulk_import.py b/netbox/tenancy/forms/bulk_import.py index f37317549..8234513ae 100644 --- a/netbox/tenancy/forms/bulk_import.py +++ b/netbox/tenancy/forms/bulk_import.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ from netbox.forms import NetBoxModelImportForm from tenancy.models import * -from utilities.forms.fields import CSVContentTypeField, CSVModelChoiceField, SlugField +from utilities.forms.fields import CSVContentTypeField, CSVModelChoiceField, CSVModelMultipleChoiceField, SlugField __all__ = ( 'ContactAssignmentImportForm', @@ -31,7 +31,7 @@ class TenantGroupImportForm(NetBoxModelImportForm): class Meta: model = TenantGroup - fields = ('name', 'slug', 'parent', 'description', 'tags') + fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments') class TenantImportForm(NetBoxModelImportForm): @@ -65,7 +65,7 @@ class ContactGroupImportForm(NetBoxModelImportForm): class Meta: model = ContactGroup - fields = ('name', 'slug', 'parent', 'description', 'tags') + fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments') class ContactRoleImportForm(NetBoxModelImportForm): @@ -77,17 +77,16 @@ class ContactRoleImportForm(NetBoxModelImportForm): class ContactImportForm(NetBoxModelImportForm): - group = CSVModelChoiceField( - label=_('Group'), + groups = CSVModelMultipleChoiceField( queryset=ContactGroup.objects.all(), required=False, to_field_name='name', - help_text=_('Assigned group') + help_text=_('Group names separated by commas, encased with double quotes (e.g. "Group 1,Group 2")') ) class Meta: model = Contact - fields = ('name', 'title', 'phone', 'email', 'address', 'link', 'group', 'description', 'comments', 'tags') + fields = ('name', 'title', 'phone', 'email', 'address', 'link', 'groups', 'description', 'comments', 'tags') class ContactAssignmentImportForm(NetBoxModelImportForm): diff --git a/netbox/tenancy/forms/filtersets.py b/netbox/tenancy/forms/filtersets.py index 960ca45b1..6541d9693 100644 --- a/netbox/tenancy/forms/filtersets.py +++ b/netbox/tenancy/forms/filtersets.py @@ -75,7 +75,7 @@ class ContactFilterForm(NetBoxModelFilterSetForm): queryset=ContactGroup.objects.all(), required=False, null_option='None', - label=_('Group') + label=_('Groups') ) tag = TagFilterField(model) diff --git a/netbox/tenancy/forms/model_forms.py b/netbox/tenancy/forms/model_forms.py index bc18deed6..6ef9d8560 100644 --- a/netbox/tenancy/forms/model_forms.py +++ b/netbox/tenancy/forms/model_forms.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ from netbox.forms import NetBoxModelForm from tenancy.models import * -from utilities.forms.fields import CommentField, DynamicModelChoiceField, SlugField +from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField from utilities.forms.rendering import FieldSet, ObjectAttribute __all__ = ( @@ -27,6 +27,7 @@ class TenantGroupForm(NetBoxModelForm): required=False ) slug = SlugField() + comments = CommentField() fieldsets = ( FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Tenant Group')), @@ -35,7 +36,7 @@ class TenantGroupForm(NetBoxModelForm): class Meta: model = TenantGroup fields = [ - 'parent', 'name', 'slug', 'description', 'tags', + 'parent', 'name', 'slug', 'description', 'tags', 'comments' ] @@ -70,6 +71,7 @@ class ContactGroupForm(NetBoxModelForm): required=False ) slug = SlugField() + comments = CommentField() fieldsets = ( FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Contact Group')), @@ -77,7 +79,7 @@ class ContactGroupForm(NetBoxModelForm): class Meta: model = ContactGroup - fields = ('parent', 'name', 'slug', 'description', 'tags') + fields = ('parent', 'name', 'slug', 'description', 'tags', 'comments') class ContactRoleForm(NetBoxModelForm): @@ -93,8 +95,8 @@ class ContactRoleForm(NetBoxModelForm): class ContactForm(NetBoxModelForm): - group = DynamicModelChoiceField( - label=_('Group'), + groups = DynamicModelMultipleChoiceField( + label=_('Groups'), queryset=ContactGroup.objects.all(), required=False ) @@ -102,7 +104,7 @@ class ContactForm(NetBoxModelForm): fieldsets = ( FieldSet( - 'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'tags', + 'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'tags', name=_('Contact') ), ) @@ -110,7 +112,7 @@ class ContactForm(NetBoxModelForm): class Meta: model = Contact fields = ( - 'group', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'tags', + 'groups', 'name', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'tags', ) widgets = { 'address': forms.Textarea(attrs={'rows': 3}), @@ -123,7 +125,7 @@ class ContactAssignmentForm(NetBoxModelForm): queryset=ContactGroup.objects.all(), required=False, initial_params={ - 'contacts': '$contact' + 'contact': '$contact' } ) contact = DynamicModelChoiceField( diff --git a/netbox/tenancy/graphql/enums.py b/netbox/tenancy/graphql/enums.py new file mode 100644 index 000000000..98787f43b --- /dev/null +++ b/netbox/tenancy/graphql/enums.py @@ -0,0 +1,9 @@ +import strawberry + +from tenancy.choices import * + +__all__ = ( + 'ContactPriorityEnum', +) + +ContactPriorityEnum = strawberry.enum(ContactPriorityChoices.as_enum(prefix='priority')) diff --git a/netbox/tenancy/graphql/filter_mixins.py b/netbox/tenancy/graphql/filter_mixins.py new file mode 100644 index 000000000..cc4a4297c --- /dev/null +++ b/netbox/tenancy/graphql/filter_mixins.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django +from strawberry import ID + +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from netbox.graphql.filter_lookups import TreeNodeFilter + from .filters import ContactFilter, TenantFilter, TenantGroupFilter + +__all__ = ( + 'ContactFilterMixin', + 'TenancyFilterMixin', +) + + +@dataclass +class ContactFilterMixin(BaseFilterMixin): + contacts: Annotated['ContactFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class TenancyFilterMixin(BaseFilterMixin): + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + tenant_group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/tenancy/graphql/filters.py b/netbox/tenancy/graphql/filters.py index e82b1cd07..f215fd8ab 100644 --- a/netbox/tenancy/graphql/filters.py +++ b/netbox/tenancy/graphql/filters.py @@ -1,7 +1,50 @@ -import strawberry_django +from typing import Annotated, TYPE_CHECKING -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from tenancy import filtersets, models +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import ChangeLogFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import ( + NestedGroupModelFilterMixin, + OrganizationalModelFilterMixin, + PrimaryModelFilterMixin, +) +from tenancy import models +from .filter_mixins import ContactFilterMixin + +if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from circuits.graphql.filters import CircuitFilter, CircuitGroupFilter, VirtualCircuitFilter + from dcim.graphql.filters import ( + CableFilter, + DeviceFilter, + LocationFilter, + PowerFeedFilter, + RackFilter, + RackReservationFilter, + SiteFilter, + VirtualDeviceContextFilter, + ) + from ipam.graphql.filters import ( + AggregateFilter, + ASNFilter, + ASNRangeFilter, + IPAddressFilter, + IPRangeFilter, + PrefixFilter, + RouteTargetFilter, + VLANFilter, + VLANGroupFilter, + VRFFilter, + ) + from netbox.graphql.filter_lookups import TreeNodeFilter + from wireless.graphql.filters import WirelessLANFilter, WirelessLinkFilter + from virtualization.graphql.filters import ClusterFilter, VirtualMachineFilter + from vpn.graphql.filters import L2VPNFilter, TunnelFilter + from .enums import * __all__ = ( 'TenantFilter', @@ -14,36 +57,140 @@ __all__ = ( @strawberry_django.filter(models.Tenant, lookups=True) -@autotype_decorator(filtersets.TenantFilterSet) -class TenantFilter(BaseFilterMixin): - pass +class TenantFilter(PrimaryModelFilterMixin, ContactFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + # Reverse relations + aggregates: Annotated['AggregateFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + asn_ranges: Annotated['ASNRangeFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cables: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + circuit_groups: Annotated['CircuitGroupFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + circuits: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + clusters: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + devices: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ip_ranges: Annotated['IPRangeFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + l2vpns: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + locations: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_feeds: Annotated['PowerFeedFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + racks: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + rackreservations: Annotated['RackReservationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + route_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + tunnels: Annotated['TunnelFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + vdcs: Annotated['VirtualDeviceContextFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_machines: Annotated['VirtualMachineFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlans: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + virtual_circuits: Annotated['VirtualCircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + wireless_lans: Annotated['WirelessLANFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + wireless_links: Annotated['WirelessLinkFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.TenantGroup, lookups=True) -@autotype_decorator(filtersets.TenantGroupFilterSet) -class TenantGroupFilter(BaseFilterMixin): - pass +class TenantGroupFilter(OrganizationalModelFilterMixin): + parent: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry.UNSET + tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + children: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters'), True] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Contact, lookups=True) -@autotype_decorator(filtersets.ContactFilterSet) -class ContactFilter(BaseFilterMixin): - pass +class ContactFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + title: FilterLookup[str] | None = strawberry_django.filter_field() + phone: FilterLookup[str] | None = strawberry_django.filter_field() + email: FilterLookup[str] | None = strawberry_django.filter_field() + address: FilterLookup[str] | None = strawberry_django.filter_field() + link: FilterLookup[str] | None = strawberry_django.filter_field() + groups: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assignments: Annotated['ContactAssignmentFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ContactRole, lookups=True) -@autotype_decorator(filtersets.ContactRoleFilterSet) -class ContactRoleFilter(BaseFilterMixin): +class ContactRoleFilter(OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.ContactGroup, lookups=True) -@autotype_decorator(filtersets.ContactGroupFilterSet) -class ContactGroupFilter(BaseFilterMixin): - pass +class ContactGroupFilter(NestedGroupModelFilterMixin): + parent: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ContactAssignment, lookups=True) -@autotype_decorator(filtersets.ContactAssignmentFilterSet) -class ContactAssignmentFilter(BaseFilterMixin): - pass +class ContactAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + object_id: ID | None = strawberry_django.filter_field() + contact: Annotated['ContactFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + contact_id: ID | None = strawberry_django.filter_field() + role: Annotated['ContactRoleFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + priority: Annotated['ContactPriorityEnum', strawberry.lazy('tenancy.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/tenancy/graphql/mixins.py b/netbox/tenancy/graphql/mixins.py index 9cdba100e..9437a06f2 100644 --- a/netbox/tenancy/graphql/mixins.py +++ b/netbox/tenancy/graphql/mixins.py @@ -9,5 +9,4 @@ __all__ = ( @strawberry.type class ContactAssignmentsMixin: - assignments: List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]] # noqa: F821 diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index b07a14c80..a3713da93 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django @@ -6,8 +6,36 @@ import strawberry_django from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType from tenancy import models -from .mixins import ContactAssignmentsMixin from .filters import * +from .mixins import ContactAssignmentsMixin + +if TYPE_CHECKING: + from circuits.graphql.types import CircuitType + from dcim.graphql.types import ( + CableType, + DeviceType, + LocationType, + PowerFeedType, + RackType, + RackReservationType, + SiteType, + VirtualDeviceContextType, + ) + from ipam.graphql.types import ( + AggregateType, + ASNType, + ASNRangeType, + IPAddressType, + IPRangeType, + PrefixType, + RouteTargetType, + VLANType, + VRFType, + ) + from netbox.graphql.types import ContentTypeType + from wireless.graphql.types import WirelessLANType, WirelessLinkType + from virtualization.graphql.types import ClusterType, VirtualMachineType + from vpn.graphql.types import L2VPNType, TunnelType __all__ = ( 'ContactAssignmentType', @@ -26,47 +54,48 @@ __all__ = ( @strawberry_django.type( models.Tenant, fields='__all__', - filters=TenantFilter + filters=TenantFilter, + pagination=True ) class TenantType(ContactsMixin, NetBoxObjectType): - group: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None - - asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]] - circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]] - sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]] - vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]] - wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]] - route_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]] - locations: List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]] - ip_ranges: List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]] - rackreservations: List[Annotated["RackReservationType", strawberry.lazy('dcim.graphql.types')]] - racks: List[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]] - vdcs: List[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]] - prefixes: List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]] - cables: List[Annotated["CableType", strawberry.lazy('dcim.graphql.types')]] - virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]] - vrfs: List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]] - asn_ranges: List[Annotated["ASNRangeType", strawberry.lazy('ipam.graphql.types')]] - wireless_links: List[Annotated["WirelessLinkType", strawberry.lazy('wireless.graphql.types')]] - aggregates: List[Annotated["AggregateType", strawberry.lazy('ipam.graphql.types')]] - power_feeds: List[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]] - devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]] - tunnels: List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]] - ip_addresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]] - clusters: List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]] - l2vpns: List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]] + group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None + asns: List[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]] + circuits: List[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]] + sites: List[Annotated['SiteType', strawberry.lazy('dcim.graphql.types')]] + vlans: List[Annotated['VLANType', strawberry.lazy('ipam.graphql.types')]] + wireless_lans: List[Annotated['WirelessLANType', strawberry.lazy('wireless.graphql.types')]] + route_targets: List[Annotated['RouteTargetType', strawberry.lazy('ipam.graphql.types')]] + locations: List[Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]] + ip_ranges: List[Annotated['IPRangeType', strawberry.lazy('ipam.graphql.types')]] + rackreservations: List[Annotated['RackReservationType', strawberry.lazy('dcim.graphql.types')]] + racks: List[Annotated['RackType', strawberry.lazy('dcim.graphql.types')]] + vdcs: List[Annotated['VirtualDeviceContextType', strawberry.lazy('dcim.graphql.types')]] + prefixes: List[Annotated['PrefixType', strawberry.lazy('ipam.graphql.types')]] + cables: List[Annotated['CableType', strawberry.lazy('dcim.graphql.types')]] + virtual_machines: List[Annotated['VirtualMachineType', strawberry.lazy('virtualization.graphql.types')]] + vrfs: List[Annotated['VRFType', strawberry.lazy('ipam.graphql.types')]] + asn_ranges: List[Annotated['ASNRangeType', strawberry.lazy('ipam.graphql.types')]] + wireless_links: List[Annotated['WirelessLinkType', strawberry.lazy('wireless.graphql.types')]] + aggregates: List[Annotated['AggregateType', strawberry.lazy('ipam.graphql.types')]] + power_feeds: List[Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]] + devices: List[Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]] + tunnels: List[Annotated['TunnelType', strawberry.lazy('vpn.graphql.types')]] + ip_addresses: List[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]] + clusters: List[Annotated['ClusterType', strawberry.lazy('virtualization.graphql.types')]] + l2vpns: List[Annotated['L2VPNType', strawberry.lazy('vpn.graphql.types')]] @strawberry_django.type( models.TenantGroup, fields='__all__', - filters=TenantGroupFilter + filters=TenantGroupFilter, + pagination=True ) class TenantGroupType(OrganizationalObjectType): - parent: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None + parent: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None tenants: List[TenantType] - children: List[Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')]] + children: List[Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')]] # @@ -76,16 +105,18 @@ class TenantGroupType(OrganizationalObjectType): @strawberry_django.type( models.Contact, fields='__all__', - filters=ContactFilter + filters=ContactFilter, + pagination=True ) class ContactType(ContactAssignmentsMixin, NetBoxObjectType): - group: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None + groups: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]] @strawberry_django.type( models.ContactRole, fields='__all__', - filters=ContactRoleFilter + filters=ContactRoleFilter, + pagination=True ) class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType): pass @@ -94,21 +125,23 @@ class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType): @strawberry_django.type( models.ContactGroup, fields='__all__', - filters=ContactGroupFilter + filters=ContactGroupFilter, + pagination=True ) class ContactGroupType(OrganizationalObjectType): - parent: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None + parent: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None contacts: List[ContactType] - children: List[Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')]] + children: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]] @strawberry_django.type( models.ContactAssignment, fields='__all__', - filters=ContactAssignmentFilter + filters=ContactAssignmentFilter, + pagination=True ) class ContactAssignmentType(CustomFieldsMixin, TagsMixin, BaseObjectType): - object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None - contact: Annotated["ContactType", strawberry.lazy('tenancy.graphql.types')] | None - role: Annotated["ContactRoleType", strawberry.lazy('tenancy.graphql.types')] | None + object_type: Annotated['ContentTypeType', strawberry.lazy('netbox.graphql.types')] | None + contact: Annotated['ContactType', strawberry.lazy('tenancy.graphql.types')] | None + role: Annotated['ContactRoleType', strawberry.lazy('tenancy.graphql.types')] | None diff --git a/netbox/tenancy/migrations/0001_squashed_0012.py b/netbox/tenancy/migrations/0001_squashed_0012.py index 8f3f74d9f..d7e0817ee 100644 --- a/netbox/tenancy/migrations/0001_squashed_0012.py +++ b/netbox/tenancy/migrations/0001_squashed_0012.py @@ -9,7 +9,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('extras', '0001_initial'), + ('extras', '0001_squashed'), ] replaces = [ diff --git a/netbox/tenancy/migrations/0012_contactassignment_custom_fields.py b/netbox/tenancy/migrations/0012_contactassignment_custom_fields.py index 7f681fd91..849386624 100644 --- a/netbox/tenancy/migrations/0012_contactassignment_custom_fields.py +++ b/netbox/tenancy/migrations/0012_contactassignment_custom_fields.py @@ -6,7 +6,7 @@ import utilities.json class Migration(migrations.Migration): dependencies = [ - ('tenancy', '0011_contactassignment_tags'), + ('tenancy', '0002_squashed_0011'), ] operations = [ diff --git a/netbox/tenancy/migrations/0018_contact_groups.py b/netbox/tenancy/migrations/0018_contact_groups.py new file mode 100644 index 000000000..11030eb49 --- /dev/null +++ b/netbox/tenancy/migrations/0018_contact_groups.py @@ -0,0 +1,68 @@ +import django.db.models.deletion +from django.db import migrations, models + + +def migrate_contact_groups(apps, schema_editor): + Contacts = apps.get_model('tenancy', 'Contact') + + qs = Contacts.objects.filter(group__isnull=False) + for contact in qs: + contact.groups.add(contact.group) + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenancy', '0017_natural_ordering'), + ] + + operations = [ + migrations.CreateModel( + name='ContactGroupMembership', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ], + options={ + 'verbose_name': 'contact group membership', + 'verbose_name_plural': 'contact group memberships', + }, + ), + migrations.RemoveConstraint( + model_name='contact', + name='tenancy_contact_unique_group_name', + ), + migrations.AddField( + model_name='contactgroupmembership', + name='contact', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='tenancy.contact' + ), + ), + migrations.AddField( + model_name='contactgroupmembership', + name='group', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name='+', to='tenancy.contactgroup' + ), + ), + migrations.AddField( + model_name='contact', + name='groups', + field=models.ManyToManyField( + blank=True, + related_name='contacts', + related_query_name='contact', + through='tenancy.ContactGroupMembership', + to='tenancy.contactgroup', + ), + ), + migrations.AddConstraint( + model_name='contactgroupmembership', + constraint=models.UniqueConstraint(fields=('group', 'contact'), name='unique_group_name'), + ), + migrations.RunPython(code=migrate_contact_groups, reverse_code=migrations.RunPython.noop), + migrations.RemoveField( + model_name='contact', + name='group', + ), + ] diff --git a/netbox/tenancy/migrations/0019_contactgroup_comments_tenantgroup_comments.py b/netbox/tenancy/migrations/0019_contactgroup_comments_tenantgroup_comments.py new file mode 100644 index 000000000..eee2dc351 --- /dev/null +++ b/netbox/tenancy/migrations/0019_contactgroup_comments_tenantgroup_comments.py @@ -0,0 +1,21 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenancy', '0018_contact_groups'), + ] + + operations = [ + migrations.AddField( + model_name='contactgroup', + name='comments', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='tenantgroup', + name='comments', + field=models.TextField(blank=True), + ), + ] diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index 3969c8317..5f39fe0db 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -13,6 +13,7 @@ __all__ = ( 'ContactAssignment', 'Contact', 'ContactGroup', + 'ContactGroupMembership', 'ContactRole', ) @@ -47,12 +48,12 @@ class Contact(PrimaryModel): """ Contact information for a particular object(s) in NetBox. """ - group = models.ForeignKey( + groups = models.ManyToManyField( to='tenancy.ContactGroup', - on_delete=models.SET_NULL, related_name='contacts', - blank=True, - null=True + through='tenancy.ContactGroupMembership', + related_query_name='contact', + blank=True ) name = models.CharField( verbose_name=_('name'), @@ -84,17 +85,11 @@ class Contact(PrimaryModel): ) clone_fields = ( - 'group', 'name', 'title', 'phone', 'email', 'address', 'link', + 'groups', 'name', 'title', 'phone', 'email', 'address', 'link', ) class Meta: ordering = ['name'] - constraints = ( - models.UniqueConstraint( - fields=('group', 'name'), - name='%(app_label)s_%(class)s_unique_group_name' - ), - ) verbose_name = _('contact') verbose_name_plural = _('contacts') @@ -102,6 +97,18 @@ class Contact(PrimaryModel): return self.name +class ContactGroupMembership(models.Model): + group = models.ForeignKey(ContactGroup, related_name="+", on_delete=models.CASCADE) + contact = models.ForeignKey(Contact, related_name="+", on_delete=models.CASCADE) + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['group', 'contact'], name='unique_group_name') + ] + verbose_name = _('contact group membership') + verbose_name_plural = _('contact group memberships') + + class ContactAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel): object_type = models.ForeignKey( to='contenttypes.ContentType', diff --git a/netbox/tenancy/search.py b/netbox/tenancy/search.py index 56903d6b1..f9441c974 100644 --- a/netbox/tenancy/search.py +++ b/netbox/tenancy/search.py @@ -25,6 +25,7 @@ class ContactGroupIndex(SearchIndex): ('name', 100), ('slug', 110), ('description', 500), + ('comments', 5000), ) display_attrs = ('description',) @@ -59,5 +60,6 @@ class TenantGroupIndex(SearchIndex): ('name', 100), ('slug', 110), ('description', 500), + ('comments', 5000), ) display_attrs = ('description',) diff --git a/netbox/tenancy/tables/contacts.py b/netbox/tenancy/tables/contacts.py index 5af15a97b..45762268e 100644 --- a/netbox/tenancy/tables/contacts.py +++ b/netbox/tenancy/tables/contacts.py @@ -27,11 +27,15 @@ class ContactGroupTable(NetBoxTable): tags = columns.TagColumn( url_name='tenancy:contactgroup_list' ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments'), + ) class Meta(NetBoxTable.Meta): model = ContactGroup fields = ( - 'pk', 'name', 'contact_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'name', 'contact_count', 'description', 'comments', 'slug', 'tags', 'created', + 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'contact_count', 'description') @@ -56,9 +60,9 @@ class ContactTable(NetBoxTable): verbose_name=_('Name'), linkify=True ) - group = tables.Column( - verbose_name=_('Group'), - linkify=True + groups = columns.ManyToManyColumn( + verbose_name=_('Groups'), + linkify_item=('tenancy:contactgroup', {'pk': tables.A('pk')}) ) phone = tables.Column( verbose_name=_('Phone'), @@ -79,10 +83,10 @@ class ContactTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = Contact fields = ( - 'pk', 'name', 'group', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', + 'pk', 'name', 'groups', 'title', 'phone', 'email', 'address', 'link', 'description', 'comments', 'assignment_count', 'tags', 'created', 'last_updated', ) - default_columns = ('pk', 'name', 'group', 'assignment_count', 'title', 'phone', 'email') + default_columns = ('pk', 'name', 'groups', 'assignment_count', 'title', 'phone', 'email') class ContactAssignmentTable(NetBoxTable): diff --git a/netbox/tenancy/tables/tenants.py b/netbox/tenancy/tables/tenants.py index a10133a64..70f263dbe 100644 --- a/netbox/tenancy/tables/tenants.py +++ b/netbox/tenancy/tables/tenants.py @@ -24,11 +24,15 @@ class TenantGroupTable(NetBoxTable): tags = columns.TagColumn( url_name='tenancy:tenantgroup_list' ) + comments = columns.MarkdownColumn( + verbose_name=_('Comments'), + ) class Meta(NetBoxTable.Meta): model = TenantGroup fields = ( - 'pk', 'id', 'name', 'tenant_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'id', 'name', 'tenant_count', 'description', 'comments', 'slug', 'tags', 'created', + 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'tenant_count', 'description') diff --git a/netbox/tenancy/tests/test_api.py b/netbox/tenancy/tests/test_api.py index c32ad3826..55a54c91f 100644 --- a/netbox/tenancy/tests/test_api.py +++ b/netbox/tenancy/tests/test_api.py @@ -21,6 +21,7 @@ class TenantGroupTest(APIViewTestCases.APIViewTestCase): brief_fields = ['_depth', 'description', 'display', 'id', 'name', 'slug', 'tenant_count', 'url'] bulk_update_data = { 'description': 'New description', + 'comments': 'New Comment', } @classmethod @@ -28,12 +29,17 @@ class TenantGroupTest(APIViewTestCases.APIViewTestCase): parent_tenant_groups = ( TenantGroup.objects.create(name='Parent Tenant Group 1', slug='parent-tenant-group-1'), - TenantGroup.objects.create(name='Parent Tenant Group 2', slug='parent-tenant-group-2'), + TenantGroup.objects.create( + name='Parent Tenant Group 2', slug='parent-tenant-group-2', comments='Parent Group 2 comment', + ), ) TenantGroup.objects.create(name='Tenant Group 1', slug='tenant-group-1', parent=parent_tenant_groups[0]) TenantGroup.objects.create(name='Tenant Group 2', slug='tenant-group-2', parent=parent_tenant_groups[0]) - TenantGroup.objects.create(name='Tenant Group 3', slug='tenant-group-3', parent=parent_tenant_groups[0]) + TenantGroup.objects.create( + name='Tenant Group 3', slug='tenant-group-3', parent=parent_tenant_groups[0], + comments='Tenant Group 3 comment' + ) cls.create_data = [ { @@ -50,6 +56,7 @@ class TenantGroupTest(APIViewTestCases.APIViewTestCase): 'name': 'Tenant Group 6', 'slug': 'tenant-group-6', 'parent': parent_tenant_groups[1].pk, + 'comments': 'Tenant Group 6 comment', }, ] @@ -107,13 +114,18 @@ class ContactGroupTest(APIViewTestCases.APIViewTestCase): def setUpTestData(cls): parent_contact_groups = ( - ContactGroup.objects.create(name='Parent Contact Group 1', slug='parent-contact-group-1'), + ContactGroup.objects.create( + name='Parent Contact Group 1', slug='parent-contact-group-1', comments='Parent 1 comment' + ), ContactGroup.objects.create(name='Parent Contact Group 2', slug='parent-contact-group-2'), ) ContactGroup.objects.create(name='Contact Group 1', slug='contact-group-1', parent=parent_contact_groups[0]) ContactGroup.objects.create(name='Contact Group 2', slug='contact-group-2', parent=parent_contact_groups[0]) - ContactGroup.objects.create(name='Contact Group 3', slug='contact-group-3', parent=parent_contact_groups[0]) + ContactGroup.objects.create( + name='Contact Group 3', slug='contact-group-3', parent=parent_contact_groups[0], + comments='Child Group 3 comment', + ) cls.create_data = [ { @@ -125,11 +137,13 @@ class ContactGroupTest(APIViewTestCases.APIViewTestCase): 'name': 'Contact Group 5', 'slug': 'contact-group-5', 'parent': parent_contact_groups[1].pk, + 'comments': '', }, { 'name': 'Contact Group 6', 'slug': 'contact-group-6', 'parent': parent_contact_groups[1].pk, + 'comments': 'Child Group 6 comment', }, ] @@ -170,7 +184,7 @@ class ContactTest(APIViewTestCases.APIViewTestCase): model = Contact brief_fields = ['description', 'display', 'id', 'name', 'url'] bulk_update_data = { - 'group': None, + 'groups': [], 'comments': 'New comments', } @@ -183,20 +197,22 @@ class ContactTest(APIViewTestCases.APIViewTestCase): ) contacts = ( - Contact(name='Contact 1', group=contact_groups[0]), - Contact(name='Contact 2', group=contact_groups[0]), - Contact(name='Contact 3', group=contact_groups[0]), + Contact(name='Contact 1'), + Contact(name='Contact 2'), + Contact(name='Contact 3'), ) Contact.objects.bulk_create(contacts) + contacts[0].groups.add(contact_groups[0]) + contacts[1].groups.add(contact_groups[0]) + contacts[2].groups.add(contact_groups[0]) cls.create_data = [ { 'name': 'Contact 4', - 'group': contact_groups[1].pk, + 'groups': [contact_groups[1].pk], }, { 'name': 'Contact 5', - 'group': contact_groups[1].pk, }, { 'name': 'Contact 6', diff --git a/netbox/tenancy/tests/test_filtersets.py b/netbox/tenancy/tests/test_filtersets.py index f6890a3d4..fcb354079 100644 --- a/netbox/tenancy/tests/test_filtersets.py +++ b/netbox/tenancy/tests/test_filtersets.py @@ -16,7 +16,7 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests): parent_tenant_groups = ( TenantGroup(name='Tenant Group 1', slug='tenant-group-1'), - TenantGroup(name='Tenant Group 2', slug='tenant-group-2'), + TenantGroup(name='Tenant Group 2', slug='tenant-group-2', comments='Parent group 2 comment'), TenantGroup(name='Tenant Group 3', slug='tenant-group-3'), ) for tenant_group in parent_tenant_groups: @@ -27,7 +27,8 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests): name='Tenant Group 1A', slug='tenant-group-1a', parent=parent_tenant_groups[0], - description='foobar1' + description='foobar1', + comments='Tenant Group 1A comment', ), TenantGroup( name='Tenant Group 2A', @@ -48,7 +49,10 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests): child_tenant_groups = ( TenantGroup(name='Tenant Group 1A1', slug='tenant-group-1a1', parent=tenant_groups[0]), TenantGroup(name='Tenant Group 2A1', slug='tenant-group-2a1', parent=tenant_groups[1]), - TenantGroup(name='Tenant Group 3A1', slug='tenant-group-3a1', parent=tenant_groups[2]), + TenantGroup( + name='Tenant Group 3A1', slug='tenant-group-3a1', parent=tenant_groups[2], + comments='Tenant Group 3A1 comment', + ), ) for tenant_group in child_tenant_groups: tenant_group.save() @@ -57,6 +61,13 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_q_comments(self): + params = {'q': 'parent'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'q': 'comment'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + def test_name(self): params = {'name': ['Tenant Group 1', 'Tenant Group 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -139,7 +150,7 @@ class ContactGroupTestCase(TestCase, ChangeLoggedFilterSetTests): parent_contact_groups = ( ContactGroup(name='Contact Group 1', slug='contact-group-1'), - ContactGroup(name='Contact Group 2', slug='contact-group-2'), + ContactGroup(name='Contact Group 2', slug='contact-group-2', comments='Parent group 2'), ContactGroup(name='Contact Group 3', slug='contact-group-3'), ) for contact_group in parent_contact_groups: @@ -162,14 +173,18 @@ class ContactGroupTestCase(TestCase, ChangeLoggedFilterSetTests): name='Contact Group 3A', slug='contact-group-3a', parent=parent_contact_groups[2], - description='foobar3' + description='foobar3', + comments='Contact Group 3A comment, not a parent', ), ) for contact_group in contact_groups: contact_group.save() child_contact_groups = ( - ContactGroup(name='Contact Group 1A1', slug='contact-group-1a1', parent=contact_groups[0]), + ContactGroup( + name='Contact Group 1A1', slug='contact-group-1a1', parent=contact_groups[0], + comments='Contact Group 1A1 comment', + ), ContactGroup(name='Contact Group 2A1', slug='contact-group-2a1', parent=contact_groups[1]), ContactGroup(name='Contact Group 3A1', slug='contact-group-3a1', parent=contact_groups[2]), ) @@ -180,6 +195,13 @@ class ContactGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_q_comments(self): + params = {'q': 'parent'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + params = {'q': '1A1'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_name(self): params = {'name': ['Contact Group 1', 'Contact Group 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -241,6 +263,7 @@ class ContactRoleTestCase(TestCase, ChangeLoggedFilterSetTests): class ContactTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = Contact.objects.all() filterset = ContactFilterSet + ignore_fields = ('groups',) @classmethod def setUpTestData(cls): @@ -254,11 +277,14 @@ class ContactTestCase(TestCase, ChangeLoggedFilterSetTests): contactgroup.save() contacts = ( - Contact(name='Contact 1', group=contact_groups[0], description='foobar1'), - Contact(name='Contact 2', group=contact_groups[1], description='foobar2'), - Contact(name='Contact 3', group=contact_groups[2], description='foobar3'), + Contact(name='Contact 1', description='foobar1'), + Contact(name='Contact 2', description='foobar2'), + Contact(name='Contact 3', description='foobar3'), ) Contact.objects.bulk_create(contacts) + contacts[0].groups.add(contact_groups[0]) + contacts[1].groups.add(contact_groups[1]) + contacts[2].groups.add(contact_groups[2]) def test_q(self): params = {'q': 'foobar1'} @@ -311,11 +337,14 @@ class ContactAssignmentTestCase(TestCase, ChangeLoggedFilterSetTests): ContactRole.objects.bulk_create(contact_roles) contacts = ( - Contact(name='Contact 1', group=contact_groups[0]), - Contact(name='Contact 2', group=contact_groups[1]), - Contact(name='Contact 3', group=contact_groups[2]), + Contact(name='Contact 1'), + Contact(name='Contact 2'), + Contact(name='Contact 3'), ) Contact.objects.bulk_create(contacts) + contacts[0].groups.add(contact_groups[0]) + contacts[1].groups.add(contact_groups[1]) + contacts[2].groups.add(contact_groups[2]) assignments = ( ContactAssignment(object=sites[0], contact=contacts[0], role=contact_roles[0]), diff --git a/netbox/tenancy/tests/test_views.py b/netbox/tenancy/tests/test_views.py index cbdecc0d0..4d1a45a82 100644 --- a/netbox/tenancy/tests/test_views.py +++ b/netbox/tenancy/tests/test_views.py @@ -15,7 +15,7 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): tenant_groups = ( TenantGroup(name='Tenant Group 1', slug='tenant-group-1'), - TenantGroup(name='Tenant Group 2', slug='tenant-group-2'), + TenantGroup(name='Tenant Group 2', slug='tenant-group-2', comments='Tenant Group 2 comment'), TenantGroup(name='Tenant Group 3', slug='tenant-group-3'), ) for tenanantgroup in tenant_groups: @@ -28,24 +28,26 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'slug': 'tenant-group-x', 'description': 'A new tenant group', 'tags': [t.pk for t in tags], + 'comments': 'Tenant Group X comment', } cls.csv_data = ( - "name,slug,description", - "Tenant Group 4,tenant-group-4,Fourth tenant group", - "Tenant Group 5,tenant-group-5,Fifth tenant group", - "Tenant Group 6,tenant-group-6,Sixth tenant group", + "name,slug,description,comments", + "Tenant Group 4,tenant-group-4,Fourth tenant group,", + "Tenant Group 5,tenant-group-5,Fifth tenant group,", + "Tenant Group 6,tenant-group-6,Sixth tenant group,Sixth tenant group comment", ) cls.csv_update_data = ( - "id,name,description", - f"{tenant_groups[0].pk},Tenant Group 7,Fourth tenant group7", - f"{tenant_groups[1].pk},Tenant Group 8,Fifth tenant group8", - f"{tenant_groups[2].pk},Tenant Group 0,Sixth tenant group9", + "id,name,description,comments", + f"{tenant_groups[0].pk},Tenant Group 7,Fourth tenant group7,Group 7 comment", + f"{tenant_groups[1].pk},Tenant Group 8,Fifth tenant group8,", + f"{tenant_groups[2].pk},Tenant Group 0,Sixth tenant group9,", ) cls.bulk_edit_data = { 'description': 'New description', + 'comments': 'New comment', } @@ -106,7 +108,7 @@ class ContactGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): def setUpTestData(cls): contact_groups = ( - ContactGroup(name='Contact Group 1', slug='contact-group-1'), + ContactGroup(name='Contact Group 1', slug='contact-group-1', comments='Comment 1'), ContactGroup(name='Contact Group 2', slug='contact-group-2'), ContactGroup(name='Contact Group 3', slug='contact-group-3'), ) @@ -120,24 +122,26 @@ class ContactGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'slug': 'contact-group-x', 'description': 'A new contact group', 'tags': [t.pk for t in tags], + 'comments': 'Form data comment', } cls.csv_data = ( - "name,slug,description", - "Contact Group 4,contact-group-4,Fourth contact group", - "Contact Group 5,contact-group-5,Fifth contact group", - "Contact Group 6,contact-group-6,Sixth contact group", + "name,slug,description,comments", + "Contact Group 4,contact-group-4,Fourth contact group,", + "Contact Group 5,contact-group-5,Fifth contact group,Fifth comment", + "Contact Group 6,contact-group-6,Sixth contact group,", ) cls.csv_update_data = ( - "id,name,description", - f"{contact_groups[0].pk},Contact Group 7,Fourth contact group7", - f"{contact_groups[1].pk},Contact Group 8,Fifth contact group8", - f"{contact_groups[2].pk},Contact Group 0,Sixth contact group9", + "id,name,description,comments", + f"{contact_groups[0].pk},Contact Group 7,Fourth contact group7,", + f"{contact_groups[1].pk},Contact Group 8,Fifth contact group8,Group 8 comment", + f"{contact_groups[2].pk},Contact Group 0,Sixth contact group9,", ) cls.bulk_edit_data = { 'description': 'New description', + 'comments': 'Bulk update comment', } @@ -196,37 +200,40 @@ class ContactTestCase(ViewTestCases.PrimaryObjectViewTestCase): contactgroup.save() contacts = ( - Contact(name='Contact 1', group=contact_groups[0]), - Contact(name='Contact 2', group=contact_groups[0]), - Contact(name='Contact 3', group=contact_groups[0]), + Contact(name='Contact 1'), + Contact(name='Contact 2'), + Contact(name='Contact 3'), ) Contact.objects.bulk_create(contacts) + contacts[0].groups.add(contact_groups[0]) + contacts[1].groups.add(contact_groups[1]) tags = create_tags('Alpha', 'Bravo', 'Charlie') cls.form_data = { 'name': 'Contact X', - 'group': contact_groups[1].pk, + 'groups': [contact_groups[1].pk], 'comments': 'Some comments', 'tags': [t.pk for t in tags], } cls.csv_data = ( - "group,name", - "Contact Group 1,Contact 4", - "Contact Group 1,Contact 5", - "Contact Group 1,Contact 6", + "name", + "groups", + "Contact 4", + "Contact 5", + "Contact 6", ) cls.csv_update_data = ( - "id,name,comments", - f"{contacts[0].pk},Contact Group 7,New comments 7", - f"{contacts[1].pk},Contact Group 8,New comments 8", - f"{contacts[2].pk},Contact Group 9,New comments 9", + "id,name,groups,comments", + f'{contacts[0].pk},Contact 7,"Contact Group 1,Contact Group 2",New comments 7', + f'{contacts[1].pk},Contact 8,"Contact Group 1",New comments 8', + f'{contacts[2].pk},Contact 9,"Contact Group 1",New comments 9', ) cls.bulk_edit_data = { - 'group': contact_groups[1].pk, + 'description': "New description", } diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index feb0baa15..aca0a14eb 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -1,43 +1,13 @@ from django.contrib.contenttypes.models import ContentType from django.shortcuts import get_object_or_404 -from django.utils.translation import gettext_lazy as _ from netbox.views import generic from utilities.query import count_related -from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view +from utilities.views import GetRelatedModelsMixin, register_model_view from . import filtersets, forms, tables from .models import * -class ObjectContactsView(generic.ObjectChildrenView): - child_model = ContactAssignment - table = tables.ContactAssignmentTable - filterset = filtersets.ContactAssignmentFilterSet - filterset_form = forms.ContactAssignmentFilterForm - template_name = 'tenancy/object_contacts.html' - tab = ViewTab( - label=_('Contacts'), - badge=lambda obj: obj.contacts.count(), - permission='tenancy.view_contactassignment', - weight=5000 - ) - - def get_children(self, request, parent): - return ContactAssignment.objects.restrict(request.user, 'view').filter( - object_type=ContentType.objects.get_for_model(parent), - object_id=parent.pk - ).order_by('priority', 'contact', 'role') - - def get_table(self, *args, **kwargs): - table = super().get_table(*args, **kwargs) - - # Hide object columns - table.columns.hide('object_type') - table.columns.hide('object') - - return table - - # # Tenant groups # @@ -168,11 +138,6 @@ class TenantBulkDeleteView(generic.BulkDeleteView): table = tables.TenantTable -@register_model_view(Tenant, 'contacts') -class TenantContactsView(ObjectContactsView): - queryset = Tenant.objects.all() - - # # Contact groups # @@ -182,7 +147,7 @@ class ContactGroupListView(generic.ObjectListView): queryset = ContactGroup.objects.add_related_count( ContactGroup.objects.all(), Contact, - 'group', + 'groups', 'contact_count', cumulative=True ) @@ -199,7 +164,13 @@ class ContactGroupView(GetRelatedModelsMixin, generic.ObjectView): groups = instance.get_descendants(include_self=True) return { - 'related_models': self.get_related_models(request, groups), + 'related_models': self.get_related_models( + request, + groups, + extra=( + (Contact.objects.restrict(request.user, 'view').filter(groups__in=groups), 'group_id'), + ), + ), } @@ -226,7 +197,7 @@ class ContactGroupBulkEditView(generic.BulkEditView): queryset = ContactGroup.objects.add_related_count( ContactGroup.objects.all(), Contact, - 'group', + 'groups', 'contact_count', cumulative=True ) @@ -240,7 +211,7 @@ class ContactGroupBulkDeleteView(generic.BulkDeleteView): queryset = ContactGroup.objects.add_related_count( ContactGroup.objects.all(), Contact, - 'group', + 'groups', 'contact_count', cumulative=True ) @@ -349,6 +320,15 @@ class ContactBulkEditView(generic.BulkEditView): table = tables.ContactTable form = forms.ContactBulkEditForm + def post_save_operations(self, form, obj): + super().post_save_operations(form, obj) + + # Add/remove groups + if form.cleaned_data.get('add_groups', None): + obj.groups.add(*form.cleaned_data['add_groups']) + if form.cleaned_data.get('remove_groups', None): + obj.groups.remove(*form.cleaned_data['remove_groups']) + @register_model_view(Contact, 'bulk_delete', path='delete', detail=False) class ContactBulkDeleteView(generic.BulkDeleteView): diff --git a/netbox/translations/cs/LC_MESSAGES/django.po b/netbox/translations/cs/LC_MESSAGES/django.po index 1ad0417a8..00ca5335e 100644 --- a/netbox/translations/cs/LC_MESSAGES/django.po +++ b/netbox/translations/cs/LC_MESSAGES/django.po @@ -15,7 +15,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Czech (https://app.transifex.com/netbox-community/teams/178115/cs/)\n" @@ -68,24 +68,24 @@ msgstr "Naposledy použitý" msgid "Allowed IPs" msgstr "Povolené adresy IP" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Přihlášen jako {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Odhlásili jste se." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Vaše preference byly aktualizovány." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "Uživatelské pověření ověřené LDAP nelze v NetBoxu změnit." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Vaše heslo bylo úspěšně změněno." @@ -133,7 +133,7 @@ msgstr "Vyřazeno z provozu" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primární" @@ -230,7 +230,7 @@ msgstr "Skupina umístění (zkratka)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -993,7 +993,7 @@ msgstr "Atributy" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1092,7 +1092,7 @@ msgstr "Síť poskytovatele" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1212,9 +1212,9 @@ msgstr "Provozní role" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1316,7 +1316,7 @@ msgstr "Kontakty" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1339,7 +1339,7 @@ msgstr "Region" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1710,7 +1710,7 @@ msgstr "zakončení virtuálních obvodů" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1966,9 +1966,9 @@ msgstr "Zakončení" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2142,7 +2142,7 @@ msgstr "Týdenní" msgid "30 days" msgstr "30 dní" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Aktualizováno" @@ -2173,7 +2173,7 @@ msgstr "Zastaveno" msgid "Cancelled" msgstr "Zrušeno" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2802,49 +2802,49 @@ msgstr "ID" msgid "Interval" msgstr "Interval" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Verze" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Naposledy aktualizováno" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Minimální verze NetBoxu" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Maximální verze NetBoxu" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Nebyla nalezena žádná data pluginu" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Autor" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Nainstalováno" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certifikováno" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Zveřejněno" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Nainstalovaná verze" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Nejnovější verze" @@ -3081,8 +3081,8 @@ msgstr "Zezadu dopředu" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3211,7 +3211,7 @@ msgstr "Virtuální" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3222,7 +3222,7 @@ msgid "Virtual interfaces" msgstr "Virtuální rozhraní" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3609,7 +3609,7 @@ msgstr "Je plná hloubka" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3758,7 +3758,7 @@ msgstr "Přiřazené VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3779,7 +3779,7 @@ msgstr "Přiřazené VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3817,7 +3817,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Zásady překladu VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3855,7 +3855,7 @@ msgstr "Rozhraní LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC adresa" @@ -3863,14 +3863,14 @@ msgstr "MAC adresa" msgid "Primary MAC address (ID)" msgstr "Primární MAC adresa (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Primární MAC adresa" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Kontext virtuálního zařízení" @@ -3947,8 +3947,8 @@ msgstr "Značky" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3995,8 +3995,8 @@ msgstr "Časové pásmo" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4159,7 +4159,7 @@ msgstr "Proudění vzduchu" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4175,7 +4175,7 @@ msgstr "Stojan" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4199,15 +4199,24 @@ msgid "Exclude from utilization" msgstr "Vyloučit z využití" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Typ zařízení" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4393,8 +4402,8 @@ msgid "Allocated power draw (watts)" msgstr "Přidělený příkon (W)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Napájecí port" @@ -4428,7 +4437,7 @@ msgid "Wireless role" msgstr "Bezdrátová role" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4447,7 +4456,7 @@ msgstr "Modul" msgid "LAG" msgstr "Agregační skupina" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Kontexty virtuálních zařízení" @@ -4475,21 +4484,21 @@ msgstr "Rychlost" msgid "Mode" msgstr "Režim" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Skupina VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "Neznačené VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4504,16 +4513,16 @@ msgstr "Přidat označené VLANy" msgid "Remove tagged VLANs" msgstr "Odstranit označené VLANy" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Služba VLAN služby Q-in-Q" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Skupina bezdrátových sítí" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4521,29 +4530,29 @@ msgid "Wireless LANs" msgstr "Bezdrátové LAN sítě" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Adresování" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operace" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4551,7 +4560,7 @@ msgid "Related Interfaces" msgstr "Související rozhraní" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4813,13 +4822,13 @@ msgstr "Místní napájecí port, který napájí tuto zásuvku" msgid "Electrical phase (for three-phase circuits)" msgstr "Elektrická fáze (pro třífázové obvody)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Nadřazené rozhraní" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4883,8 +4892,8 @@ msgstr "Bezdrátová role (AP/stanice)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} není přiřazen k zařízení {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Zadní port" @@ -5063,7 +5072,7 @@ msgstr "Typ napájení (AC/DC)" msgid "Single or three-phase" msgstr "Jednofázové nebo třífázové" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5074,7 +5083,7 @@ msgstr "Primární IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "IPv4 adresa s maskou, např. 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5241,7 +5250,7 @@ msgstr "Druh" msgid "Mgmt only" msgstr "Pouze správa" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5386,7 +5395,7 @@ msgstr "Automaticky naplnit komponenty přidružené k tomuto typu modulu" msgid "Characteristics" msgstr "Charakteristika" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5401,35 +5410,35 @@ msgstr "" "přítomen, bude automaticky nahrazen hodnotou pozice při vytváření nového " "modulu." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Šablona portu konzoly" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Šablona portu konzolového serveru" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Šablona předního portu" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Šablona rozhraní" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Šablona elektrické zásuvky" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Šablona napájecího portu" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Šablona zadního portu" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5437,14 +5446,14 @@ msgstr "Šablona zadního portu" msgid "Console Port" msgstr "Port konzoly" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Port konzolového serveru" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5455,7 +5464,7 @@ msgstr "Port konzolového serveru" msgid "Front Port" msgstr "Přední port" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5468,40 +5477,40 @@ msgstr "Přední port" msgid "Rear Port" msgstr "Zadní port" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Napájecí port" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Napájecí zásuvka" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Přiřazení komponent" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "InventoryItem lze přiřadit pouze k jedné komponentě." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Rozhraní LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtrujte sítě VLAN dostupné pro přiřazení podle skupiny." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Podřazené zařízení" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5509,37 +5518,37 @@ msgstr "" "Podřízená zařízení musí být nejprve vytvořena a přiřazena k staveništi a " "stojanu nadřazeného zařízení." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Port konzoly" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Port konzolového serveru" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Přední port" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Napájecí zásuvka" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Položka inventáře" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Role položky inventáře" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Rozhraní VM" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5557,13 +5566,13 @@ msgstr "Rozhraní VM" msgid "Virtual Machine" msgstr "Virtuální stroj" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "MAC adresu lze přiřadit pouze jednomu objektu." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5581,17 +5590,17 @@ msgstr "" "{pattern_count}." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Zadní porty" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Vyberte jedno přiřazení zadního portu pro každý vytvořený přední port." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5600,7 +5609,7 @@ msgstr "" "Počet šablon předních portů, které mají být vytvořeny ({frontport_count}), " "musí odpovídat zvolenému počtu pozic zadních portů ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5609,24 +5618,24 @@ msgstr "" "Počet předních portů, které mají být vytvořeny ({frontport_count}), musí " "odpovídat zvolenému počtu pozic zadních portů ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Členové" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Počáteční pozice" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." msgstr "Pozice prvního člena. Zvýší se o jeden pro každého dalšího člena." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Pro prvního člena virtuálnáho šasi musí být specifikována pozice." @@ -6107,6 +6116,7 @@ msgstr "označené VLAN" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q SVLAN" @@ -7310,8 +7320,8 @@ msgid "Power outlets" msgstr "Elektrické zásuvky" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7349,8 +7359,8 @@ msgid "Module Bay" msgstr "Modulová přihrádka" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7386,7 +7396,7 @@ msgstr "Přidělené losování (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP adresy" @@ -7397,7 +7407,7 @@ msgid "FHRP Groups" msgstr "Skupiny FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7484,8 +7494,8 @@ msgstr "Výška U" msgid "Instances" msgstr "Instance" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7495,8 +7505,8 @@ msgstr "Instance" msgid "Console Ports" msgstr "Porty konzoly" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7506,8 +7516,8 @@ msgstr "Porty konzoly" msgid "Console Server Ports" msgstr "Porty konzolového serveru" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7517,8 +7527,8 @@ msgstr "Porty konzolového serveru" msgid "Power Ports" msgstr "Napájecí porty" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7528,8 +7538,8 @@ msgstr "Napájecí porty" msgid "Power Outlets" msgstr "Napájecí zásuvky" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7538,8 +7548,8 @@ msgstr "Napájecí zásuvky" msgid "Front Ports" msgstr "Přední porty" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7549,16 +7559,16 @@ msgstr "Přední porty" msgid "Rear Ports" msgstr "Zadní porty" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Pozice pro zařízení" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7627,62 +7637,62 @@ msgstr "Skupiny VLAN" msgid "Test case must set peer_termination_type" msgstr "Testovací případ musí nastavit peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Odpojeno {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Rezervace" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Zařízení bez racku" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Kontext konfigurace" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Konfigurace rendrování" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Virtuální stroje" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Nainstalované zařízení {device} v zátoce {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Odstraněné zařízení {device} od zátoky {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Děti" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Přidán člen {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "Nelze odebrat hlavní zařízení {device} z virtuálního podvozku." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Odstraněno {device} z virtuálního šasi {chassis}" @@ -11587,7 +11597,7 @@ msgstr "Role položek inventáře" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC adresy" @@ -12105,7 +12115,7 @@ msgstr "Hodnota" msgid "Dummy Plugin" msgstr "Dummy Plugin" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12113,24 +12123,24 @@ msgid "" msgstr "" "Při vykreslování vybrané šablony exportu došlo k chybě ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Řádek {i}: Objekt s ID {id} neexistuje" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Ne {object_type} Byly vybrány." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Přejmenováno {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Vymazáno {count} {object_type}" @@ -12157,7 +12167,7 @@ msgstr "Synchronizovaná data pro {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Synchronizováno {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} musí implementovat get_children ()" @@ -12432,32 +12442,36 @@ msgstr "NetBox motiv" msgid "NetBox Logo" msgstr "NetBox Logo" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Dokumentace" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Dokumentace REST API" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Podpora NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Zdrojový kód" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Komunita" @@ -13467,7 +13481,7 @@ msgid "PoE Type" msgstr "Typ PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Překlad VLAN" @@ -13520,12 +13534,12 @@ msgstr "Žádná členská rozhraní" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Přidat IP adresu" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Přidat MAC adresu" @@ -15904,7 +15918,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Neznámý app_label/model_name pro {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Neplatná IP adresa nastavená pro {header}: {ip}" diff --git a/netbox/translations/da/LC_MESSAGES/django.po b/netbox/translations/da/LC_MESSAGES/django.po index 1f30f35a1..201c60740 100644 --- a/netbox/translations/da/LC_MESSAGES/django.po +++ b/netbox/translations/da/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Danish (https://app.transifex.com/netbox-community/teams/178115/da/)\n" @@ -67,24 +67,24 @@ msgstr "Sidst brugt" msgid "Allowed IPs" msgstr "Tilladte IP'er" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Logget ind som {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Du er logget ud." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Dine præferencer er blevet opdateret." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "LDAP-godkendte brugeroplysninger kan ikke ændres i NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Din adgangskode er blevet ændret." @@ -132,7 +132,7 @@ msgstr "Nedlagt" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primær" @@ -229,7 +229,7 @@ msgstr "Områdegruppe (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -992,7 +992,7 @@ msgstr "Attributter" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1091,7 +1091,7 @@ msgstr "Leverandørnetværk" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1211,9 +1211,9 @@ msgstr "Operationel rolle" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1315,7 +1315,7 @@ msgstr "Kontakter" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1338,7 +1338,7 @@ msgstr "Regionen" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1709,7 +1709,7 @@ msgstr "virtuelle kredsløbsafslutninger" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1965,9 +1965,9 @@ msgstr "Opsigelser" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2141,7 +2141,7 @@ msgstr "Ugentlig" msgid "30 days" msgstr "30 dage" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Opdateret" @@ -2172,7 +2172,7 @@ msgstr "Stoppet" msgid "Cancelled" msgstr "Annulleret" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2802,49 +2802,49 @@ msgstr "ID" msgid "Interval" msgstr "Intervaller" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Udgave" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Senest opdateret" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Minimum NetBox-version" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Maksimal NetBox-version" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Ingen plugin-data fundet" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Forfatter" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Installeret" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certificeret" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Udgivet" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Installeret version" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Seneste version" @@ -3081,8 +3081,8 @@ msgstr "Bagsiden til forsiden" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3211,7 +3211,7 @@ msgstr "Virtuel" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3222,7 +3222,7 @@ msgid "Virtual interfaces" msgstr "Virtuelle grænseflader" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3609,7 +3609,7 @@ msgstr "Er fuld dybde" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3758,7 +3758,7 @@ msgstr "Tildelt VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3779,7 +3779,7 @@ msgstr "Tildelt VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3817,7 +3817,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "VLAN-oversættelsespolitik (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3855,7 +3855,7 @@ msgstr "LAG-grænseflade (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC-adresse" @@ -3863,14 +3863,14 @@ msgstr "MAC-adresse" msgid "Primary MAC address (ID)" msgstr "Primær MAC-adresse (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Primær MAC-adresse" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Virtuel enhedskontekst" @@ -3947,8 +3947,8 @@ msgstr "Mærker" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3995,8 +3995,8 @@ msgstr "Tidszone" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4159,7 +4159,7 @@ msgstr "Luftstrøm" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4175,7 +4175,7 @@ msgstr "Rack" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4199,15 +4199,24 @@ msgid "Exclude from utilization" msgstr "Ekskluder fra udnyttelse" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Enhedstype" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4393,8 +4402,8 @@ msgid "Allocated power draw (watts)" msgstr "Allokeret forbrug (watt)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Strømstik" @@ -4428,7 +4437,7 @@ msgid "Wireless role" msgstr "Trådløs rolle" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4447,7 +4456,7 @@ msgstr "Modul" msgid "LAG" msgstr "FORSINKELSE" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Virtuelle enhedskontekster" @@ -4475,21 +4484,21 @@ msgstr "Hastighed" msgid "Mode" msgstr "Tilstand" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "VLAN-gruppe" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "Umærket VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4504,16 +4513,16 @@ msgstr "Tilføj taggede VLAN'er" msgid "Remove tagged VLANs" msgstr "Fjern mærkede VLAN'er" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Q-in-Q-service-VLAN" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Trådløs LAN-gruppe" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4521,29 +4530,29 @@ msgid "Wireless LANs" msgstr "Trådløse LAN" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Adressering" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Betjening" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4551,7 +4560,7 @@ msgid "Related Interfaces" msgstr "Relaterede grænseflader" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4811,13 +4820,13 @@ msgstr "Lokalt strømstik, der forsyner dette strømudtag" msgid "Electrical phase (for three-phase circuits)" msgstr "Elektrisk fase (til trefasede kredsløb)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Forældregrænseflade" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4883,8 +4892,8 @@ msgstr "Trådløs rolle (AP/station)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} er ikke tildelt enheden {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Bageste port" @@ -5063,7 +5072,7 @@ msgstr "Forsyningstype (AC/DC)" msgid "Single or three-phase" msgstr "Enkelt- eller trefaset" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5074,7 +5083,7 @@ msgstr "Primær IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "IP-adresse med maske, fx 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5241,7 +5250,7 @@ msgstr "Venlig" msgid "Mgmt only" msgstr "Kun Mgmt" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5387,7 +5396,7 @@ msgstr "Udfyld automatisk komponenter, der er knyttet til denne modultype" msgid "Characteristics" msgstr "Karakteristika" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5402,35 +5411,35 @@ msgstr "" "stede, erstattes automatisk med positionsværdien, når du opretter et nyt " "modul." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Konsolportskabelon" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Konsolserverportskabelon" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Frontportskabelon" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Grænsefladeskabelon" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Skabelon til strømudtag" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Strømstikskabelon" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Bagport skabelon" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5438,14 +5447,14 @@ msgstr "Bagport skabelon" msgid "Console Port" msgstr "Konsolport" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Konsolserverport" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5456,7 +5465,7 @@ msgstr "Konsolserverport" msgid "Front Port" msgstr "Frontport" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5469,40 +5478,40 @@ msgstr "Frontport" msgid "Rear Port" msgstr "Bageste port" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Strømstik" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Strømudtag" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Komponenttildeling" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "En InventoryItem kan kun tildeles til en enkelt komponent." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "LAG-grænseflade" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtrer VLAN'er, der er tilgængelige til tildeling efter gruppe." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Børneenhed" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5510,37 +5519,37 @@ msgstr "" "Underordnede enheder skal først oprettes og tildeles til den overordnede " "enheds område og rack." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Konsolport" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Konsolserverport" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Frontport" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Strømudtag" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Lagergenstand" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Lagervarrolle" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "VM-grænseflade" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5558,13 +5567,13 @@ msgstr "VM-grænseflade" msgid "Virtual Machine" msgstr "Virtuel maskine" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "En MAC-adresse kan kun tildeles et enkelt objekt." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5582,16 +5591,16 @@ msgstr "" "{pattern_count} forventes." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Bageste porte" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "Vælg en bagporttildeling for hver frontport, der oprettes." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5600,7 +5609,7 @@ msgstr "" "Antallet af frontportskabeloner, der skal oprettes ({frontport_count}) skal " "matche det valgte antal bageste portpositioner ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5609,25 +5618,25 @@ msgstr "" "Antallet af frontporte, der skal oprettes ({frontport_count}) skal matche " "det valgte antal bageste portpositioner ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Medlemmer" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Udgangsposition" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." msgstr "" "Placering af den første medlemsenhed. Stiges med en for hvert ekstra medlem." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "En stilling skal specificeres for det første VC-medlem." @@ -6113,6 +6122,7 @@ msgstr "mærkede VLAN'er" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-i-Q SVLAN" @@ -7324,8 +7334,8 @@ msgid "Power outlets" msgstr "Strømudtag" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7363,8 +7373,8 @@ msgid "Module Bay" msgstr "Modulbugt" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7400,7 +7410,7 @@ msgstr "Tildelt lodtrækning (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP-adresser" @@ -7411,7 +7421,7 @@ msgid "FHRP Groups" msgstr "FHRP Grupper" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7498,8 +7508,8 @@ msgstr "U Højde" msgid "Instances" msgstr "forekomster" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7509,8 +7519,8 @@ msgstr "forekomster" msgid "Console Ports" msgstr "Konsolporte" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7520,8 +7530,8 @@ msgstr "Konsolporte" msgid "Console Server Ports" msgstr "Konsolserverporte" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7531,8 +7541,8 @@ msgstr "Konsolserverporte" msgid "Power Ports" msgstr "Strømstik" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7542,8 +7552,8 @@ msgstr "Strømstik" msgid "Power Outlets" msgstr "Strømudtag" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7552,8 +7562,8 @@ msgstr "Strømudtag" msgid "Front Ports" msgstr "Frontporte" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7563,16 +7573,16 @@ msgstr "Frontporte" msgid "Rear Ports" msgstr "Bageste porte" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Enhedsbugter" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7641,62 +7651,62 @@ msgstr "VLAN Grupper" msgid "Test case must set peer_termination_type" msgstr "Testcase skal indstille peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Afbrudt {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Reservationer" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Enheder uden rack" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Konfigurationskontekst" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Gengivelseskonfiguration" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Virtuelle maskiner" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Installeret enhed {device} i bugten {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Fjernet enhed {device} fra bugten {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Børn" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Tilføjet medlem {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "Kan ikke fjerne masterenheden {device} fra det virtuelle chassis." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Fjernet {device} fra virtuelt chassis {chassis}" @@ -11618,7 +11628,7 @@ msgstr "Lagervareroller" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC-adresser" @@ -12139,7 +12149,7 @@ msgstr "Værdi" msgid "Dummy Plugin" msgstr "Dummy-plugin" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12148,24 +12158,24 @@ msgstr "" "Der opstod en fejl ved gengivelse af den valgte eksportskabelon " "({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Række {i}: Objekt med ID {id} findes ikke" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Nej {object_type} blev udvalgt." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Omdøbt {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Slettet {count} {object_type}" @@ -12192,7 +12202,7 @@ msgstr "Synkroniserede data for {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Synkroniseret {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} skal implementere get_children ()" @@ -12467,32 +12477,36 @@ msgstr "NetBox Motiv" msgid "NetBox Logo" msgstr "NetBox-logoet" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Dokumenter" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "REST API-dokumentation" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL-API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "NetBox Labs Support" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Kildekode" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Fællesskab" @@ -13504,7 +13518,7 @@ msgid "PoE Type" msgstr "PoE-type" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "VLAN-oversættelse" @@ -13557,12 +13571,12 @@ msgstr "Ingen medlemsgrænseflader" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Tilføj IP-adresse" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Tilføj MAC-adresse" @@ -15947,7 +15961,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Ukendt app_label/modelnavn til {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Ugyldig IP-adresse indstillet til {header}: {ip}" diff --git a/netbox/translations/de/LC_MESSAGES/django.po b/netbox/translations/de/LC_MESSAGES/django.po index e8af0197c..d50a7686d 100644 --- a/netbox/translations/de/LC_MESSAGES/django.po +++ b/netbox/translations/de/LC_MESSAGES/django.po @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: chbally, 2025\n" "Language-Team: German (https://app.transifex.com/netbox-community/teams/178115/de/)\n" @@ -71,25 +71,25 @@ msgstr "Zuletzt verwendet" msgid "Allowed IPs" msgstr "Erlaubte IP-Adressen" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Angemeldet als {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Du hast dich abgemeldet." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Ihre Einstellungen wurden aktualisiert." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Die LDAP Zugangsdaten können nicht innerhalb von NetBox geändert werden." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Dein Passwort wurde erfolgreich geändert." @@ -137,7 +137,7 @@ msgstr "Stillgelegt" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primär" @@ -234,7 +234,7 @@ msgstr "Standortgruppe (URL-Slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -997,7 +997,7 @@ msgstr "Attribute" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1096,7 +1096,7 @@ msgstr "Providernetzwerk" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1216,9 +1216,9 @@ msgstr "Operative Rolle" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1320,7 +1320,7 @@ msgstr "Kontakte" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1343,7 +1343,7 @@ msgstr "Region" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1716,7 +1716,7 @@ msgstr "virtuelle Verbindungsabschlüsse" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1972,9 +1972,9 @@ msgstr "Abschlusspunkte" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2149,7 +2149,7 @@ msgstr "Wöchentlich" msgid "30 days" msgstr "30 Tage" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Aktualisiert" @@ -2180,7 +2180,7 @@ msgstr "Gestoppt" msgid "Cancelled" msgstr "Abgebrochen" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2819,49 +2819,49 @@ msgstr "ID" msgid "Interval" msgstr "Intervall" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Version" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Zuletzt aktualisiert" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Minimale Netbox-Version" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Maximale NetBox-Version" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Keine Plugin-Daten gefunden" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Autor" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Installiert" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Zertifiziert" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Veröffentlicht" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Installierte Version" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Neuste Version" @@ -3098,8 +3098,8 @@ msgstr "Rück- zu Frontseite" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3228,7 +3228,7 @@ msgstr "Virtuell" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3239,7 +3239,7 @@ msgid "Virtual interfaces" msgstr "Virtuelle Schnittstellen" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3626,7 +3626,7 @@ msgstr "Hat volle Tiefe" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3775,7 +3775,7 @@ msgstr "Zugewiesene VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3796,7 +3796,7 @@ msgstr "Zugewiesene VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3834,7 +3834,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "VLAN-Übersetzungsrichtlinie (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3872,7 +3872,7 @@ msgstr "LAG-Schnittstelle (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC-Adresse" @@ -3880,14 +3880,14 @@ msgstr "MAC-Adresse" msgid "Primary MAC address (ID)" msgstr "Primäre MAC-Adresse (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Primäre MAC-Adresse" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Virtual Device Context" @@ -3964,8 +3964,8 @@ msgstr "Tags" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4012,8 +4012,8 @@ msgstr "Zeitzone" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4176,7 +4176,7 @@ msgstr "Luftstrom" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4192,7 +4192,7 @@ msgstr "Rack" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4216,15 +4216,24 @@ msgid "Exclude from utilization" msgstr "Von der Nutzung ausschließen" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Gerätetyp" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4410,8 +4419,8 @@ msgid "Allocated power draw (watts)" msgstr "Zugewiesene Leistungsaufnahme (Watt)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Stromanschluss" @@ -4445,7 +4454,7 @@ msgid "Wireless role" msgstr "WLAN Funktion" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4464,7 +4473,7 @@ msgstr "Modul" msgid "LAG" msgstr "LAG" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Virtual Device Contexts" @@ -4492,21 +4501,21 @@ msgstr "Geschwindigkeit" msgid "Mode" msgstr "Modus" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "VLAN-Gruppe" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "Untagged VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4521,16 +4530,16 @@ msgstr "Hinzufügen eines getaggten VLANs" msgid "Remove tagged VLANs" msgstr "Getaggte VLANs entfernen" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Q-in-Q-Dienst-VLAN" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "WLAN-Gruppe" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4538,29 +4547,29 @@ msgid "Wireless LANs" msgstr "WLANs" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Adressierung" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Dienst / Port" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4568,7 +4577,7 @@ msgid "Related Interfaces" msgstr "Verwandte Schnittstellen" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4833,13 +4842,13 @@ msgstr "Lokaler Stromanschluss, der diese Steckdose speist" msgid "Electrical phase (for three-phase circuits)" msgstr "Elektrische Phase (für dreiphasige Stromkreise)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Übergeordnete Schnittstelle" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4905,8 +4914,8 @@ msgstr "WLAN Rolle (AP/Station)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} ist dem Gerät {device} nicht zugewiesen" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Rückseitenanschluss" @@ -5091,7 +5100,7 @@ msgstr "Versorgungsart (AC/DC)" msgid "Single or three-phase" msgstr "Ein- oder Dreiphasig" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5102,7 +5111,7 @@ msgstr "Primäre IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "IPv4-Adresse mit Maske, z. B. 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5271,7 +5280,7 @@ msgstr "Art" msgid "Mgmt only" msgstr "Nur Verwaltung" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5420,7 +5429,7 @@ msgstr "" msgid "Characteristics" msgstr "Charakteristiken" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5435,35 +5444,35 @@ msgstr "" "{module}, falls vorhanden, wird beim Erstellen eines neuen " "Moduls automatisch durch den Positionswert ersetzt." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Konsolenanschlussvorlage" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Port-Vorlage für Konsolenserver" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Frontanschluss-Vorlage" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Schnittstellen-Vorlage" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Vorlage für Steckdosen" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Vorlage für Stromverteiler" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Vorlage für den hinteren Anschluss" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5471,14 +5480,14 @@ msgstr "Vorlage für den hinteren Anschluss" msgid "Console Port" msgstr "Konsolenanschluss" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Konsolenserveranschluss" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5489,7 +5498,7 @@ msgstr "Konsolenserveranschluss" msgid "Front Port" msgstr "Frontanschluss" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5502,41 +5511,41 @@ msgstr "Frontanschluss" msgid "Rear Port" msgstr "Rückanschluss" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Stromanschluss" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Stromabgang" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Komponentenzuweisung" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "" "Ein InventoryItem kann nur einer einzelnen Komponente zugewiesen werden." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "LAG-Schnittstelle" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtern Sie VLANs, die für die Zuweisung nach Gruppen verfügbar sind." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "untergeordnetes Gerät" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5544,37 +5553,37 @@ msgstr "" "Untergeordnete Geräte müssen zuerst erstellt und dem Standort und dem Rack " "des übergeordneten Geräts zugewiesen werden." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Konsolenanschluss" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Konsolenserveranschluss" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Frontanschluss" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Stromabgang" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Inventar-Artikel" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Rolle des Inventarartikels" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "VM-Schnittstelle" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5592,13 +5601,13 @@ msgstr "VM-Schnittstelle" msgid "Virtual Machine" msgstr "Virtuelle Maschine" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Eine MAC-Adresse kann nur einem einzelnen Objekt zugewiesen werden." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5616,18 +5625,18 @@ msgstr "" "{pattern_count} werden erwartet." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Rückanschlüsse" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Wählen Sie für jeden zu erstellenden Frontanschluss eine hintere Anschluss-" "Zuweisung aus." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5637,7 +5646,7 @@ msgstr "" "muss mit der ausgewählten Anzahl der hinteren Anschlusspositionen " "übereinstimmen ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5647,18 +5656,18 @@ msgstr "" "der ausgewählten Anzahl der hinteren Anschlusspositionen übereinstimmen " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Mitglieder" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Ausgangsposition" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5666,7 +5675,7 @@ msgstr "" "Position des ersten Mitgliedsgeräts. Erhöht sich für jedes weitere Mitglied " "um eins." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Für das erste VC-Mitglied muss eine Position angegeben werden." @@ -6166,6 +6175,7 @@ msgstr "tagged VLANs" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q-SVLAN" @@ -7432,8 +7442,8 @@ msgid "Power outlets" msgstr "Steckdosen" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7471,8 +7481,8 @@ msgid "Module Bay" msgstr "Moduleinsatz" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7508,7 +7518,7 @@ msgstr "Zugewiesener Stromverbrauch (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP-Adressen" @@ -7519,7 +7529,7 @@ msgid "FHRP Groups" msgstr "FHRP-Gruppen" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7606,8 +7616,8 @@ msgstr "Höhe in HE" msgid "Instances" msgstr "Instanzen" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7617,8 +7627,8 @@ msgstr "Instanzen" msgid "Console Ports" msgstr "Konsolenanschlüsse" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7628,8 +7638,8 @@ msgstr "Konsolenanschlüsse" msgid "Console Server Ports" msgstr "Konsolenserveranschlüsse" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7639,8 +7649,8 @@ msgstr "Konsolenserveranschlüsse" msgid "Power Ports" msgstr "Stromanschlüsse" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7650,8 +7660,8 @@ msgstr "Stromanschlüsse" msgid "Power Outlets" msgstr "Steckdosen" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7660,8 +7670,8 @@ msgstr "Steckdosen" msgid "Front Ports" msgstr "Frontanschlüsse" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7671,16 +7681,16 @@ msgstr "Frontanschlüsse" msgid "Rear Ports" msgstr "Rückanschlüsse" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Geräteeinsätze" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7749,64 +7759,64 @@ msgstr "VLAN-Gruppen" msgid "Test case must set peer_termination_type" msgstr "Der Testfall muss peer_termination_type setzen" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Verbindung von {count} {type} unterbrochen" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Rackreservierungen" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Nicht in einem Rack befindliche Geräte" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Konfigurationsvorlage" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Konfiguration rendern" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Virtuelle Maschinen" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Gerät {device} im Schacht {device_bay} installiert." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Gerät {device} im Schacht {device_bay} entfernt." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Untergeordnet" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Mitglied hinzugefügt {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" "Ein Hauptgerät (Master Device) {device} kann von einem virtuellen Gehäuse " "nicht entfernt werden." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "{device} vom virtuellen Gehäuse {chassis} entfernt." @@ -11805,7 +11815,7 @@ msgstr "Inventarartikelrollen" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC-Adressen" @@ -12332,7 +12342,7 @@ msgstr "Wert" msgid "Dummy Plugin" msgstr "Dummy-Plugin" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12341,24 +12351,24 @@ msgstr "" "Beim Rendern der ausgewählten Exportvorlage ist ein Fehler aufgetreten " "({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Reihe {i}: Objekt mit ID {id} existiert nicht" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Kein {object_type}ausgewählt" -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Umbenannt {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Gelöscht {count} {object_type}" @@ -12385,7 +12395,7 @@ msgstr "Daten synchronisiert für {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Synchronisiert {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} muss get_children () implementieren" @@ -12663,32 +12673,36 @@ msgstr "NetBox-Motiv" msgid "NetBox Logo" msgstr "NetBox-Logo" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Doku" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST-API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "REST-API-Dokumentation" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL-API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "NetBox Labs Support" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Quellcode" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Community" @@ -13702,7 +13716,7 @@ msgid "PoE Type" msgstr "PoE-Typ" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "VLAN-Übersetzung" @@ -13755,12 +13769,12 @@ msgstr "Keine Mitgliederschnittstellen" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "IP-Adresse hinzufügen" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "MAC-Adresse hinzufügen" @@ -16169,7 +16183,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Unbekanntes app_label/model_name für {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Ungültige IP-Adresse gesetzt für {header}: {ip}" diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index aeee0f802..a5f2aebc7 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-05-01 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 13:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,5315 +17,4934 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: netbox/account/tables.py:27 netbox/templates/account/token.html:22 -#: netbox/templates/users/token.html:17 netbox/users/forms/bulk_import.py:39 -#: netbox/users/forms/model_forms.py:112 +#: account/tables.py:27 templates/account/token.html:22 +#: templates/users/token.html:17 users/forms/bulk_import.py:39 +#: users/forms/model_forms.py:112 msgid "Key" msgstr "" -#: netbox/account/tables.py:31 netbox/users/forms/filtersets.py:132 +#: account/tables.py:31 users/forms/filtersets.py:132 msgid "Write Enabled" msgstr "" -#: netbox/account/tables.py:35 netbox/core/choices.py:102 -#: netbox/core/tables/jobs.py:29 netbox/core/tables/tasks.py:79 -#: netbox/extras/tables/tables.py:338 netbox/extras/tables/tables.py:572 -#: netbox/templates/account/token.html:43 -#: netbox/templates/core/configrevision.html:26 -#: netbox/templates/core/configrevision_restore.html:12 -#: netbox/templates/core/job.html:69 netbox/templates/core/rq_task.html:16 -#: netbox/templates/core/rq_task.html:73 -#: netbox/templates/core/rq_worker.html:14 -#: netbox/templates/extras/htmx/script_result.html:12 -#: netbox/templates/extras/journalentry.html:22 -#: netbox/templates/generic/object.html:58 -#: netbox/templates/htmx/quick_add_created.html:7 -#: netbox/templates/users/token.html:35 +#: account/tables.py:35 core/choices.py:102 core/tables/jobs.py:29 +#: core/tables/tasks.py:79 extras/tables/tables.py:379 +#: extras/tables/tables.py:627 templates/account/token.html:43 +#: templates/core/configrevision.html:26 +#: templates/core/configrevision_restore.html:12 templates/core/job.html:69 +#: templates/core/rq_task.html:16 templates/core/rq_task.html:73 +#: templates/core/rq_worker.html:14 templates/extras/htmx/script_result.html:12 +#: templates/extras/journalentry.html:22 templates/generic/object.html:58 +#: templates/htmx/quick_add_created.html:7 templates/users/token.html:35 msgid "Created" msgstr "" -#: netbox/account/tables.py:39 netbox/templates/account/token.html:47 -#: netbox/templates/users/token.html:39 netbox/users/forms/bulk_edit.py:117 -#: netbox/users/forms/filtersets.py:136 +#: account/tables.py:39 templates/account/token.html:47 +#: templates/users/token.html:39 users/forms/bulk_edit.py:117 +#: users/forms/filtersets.py:136 msgid "Expires" msgstr "" -#: netbox/account/tables.py:42 netbox/users/forms/filtersets.py:141 +#: account/tables.py:42 users/forms/filtersets.py:141 msgid "Last Used" msgstr "" -#: netbox/account/tables.py:45 netbox/templates/account/token.html:55 -#: netbox/templates/users/token.html:47 netbox/users/forms/bulk_edit.py:122 -#: netbox/users/forms/model_forms.py:124 +#: account/tables.py:45 templates/account/token.html:55 +#: templates/users/token.html:47 users/forms/bulk_edit.py:122 +#: users/forms/model_forms.py:124 msgid "Allowed IPs" msgstr "" -#: netbox/account/views.py:116 +#: account/views.py:118 #, python-brace-format msgid "Logged in as {user}." msgstr "" -#: netbox/account/views.py:172 +#: account/views.py:174 msgid "You have logged out." msgstr "" -#: netbox/account/views.py:224 +#: account/views.py:226 msgid "Your preferences have been updated." msgstr "" -#: netbox/account/views.py:252 +#: account/views.py:254 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" -#: netbox/account/views.py:267 +#: account/views.py:269 msgid "Your password has been changed successfully." msgstr "" -#: netbox/circuits/choices.py:21 netbox/dcim/choices.py:20 -#: netbox/dcim/choices.py:102 netbox/dcim/choices.py:185 -#: netbox/dcim/choices.py:237 netbox/dcim/choices.py:1542 -#: netbox/dcim/choices.py:1600 netbox/dcim/choices.py:1650 -#: netbox/dcim/choices.py:1672 netbox/virtualization/choices.py:20 -#: netbox/virtualization/choices.py:46 netbox/vpn/choices.py:18 +#: circuits/choices.py:21 dcim/choices.py:20 dcim/choices.py:102 +#: dcim/choices.py:186 dcim/choices.py:239 dcim/choices.py:1545 +#: dcim/choices.py:1603 dcim/choices.py:1670 dcim/choices.py:1692 +#: virtualization/choices.py:20 virtualization/choices.py:46 vpn/choices.py:18 +#: vpn/choices.py:281 msgid "Planned" msgstr "" -#: netbox/circuits/choices.py:22 netbox/netbox/navigation/menu.py:326 +#: circuits/choices.py:22 netbox/navigation/menu.py:327 msgid "Provisioning" msgstr "" -#: netbox/circuits/choices.py:23 netbox/core/tables/tasks.py:22 -#: netbox/dcim/choices.py:22 netbox/dcim/choices.py:103 -#: netbox/dcim/choices.py:184 netbox/dcim/choices.py:236 -#: netbox/dcim/choices.py:1599 netbox/dcim/choices.py:1649 -#: netbox/dcim/choices.py:1671 netbox/extras/tables/tables.py:498 -#: netbox/ipam/choices.py:31 netbox/ipam/choices.py:49 -#: netbox/ipam/choices.py:69 netbox/ipam/choices.py:154 -#: netbox/templates/extras/configcontext.html:25 -#: netbox/templates/users/user.html:37 netbox/users/forms/bulk_edit.py:38 -#: netbox/virtualization/choices.py:22 netbox/virtualization/choices.py:45 -#: netbox/vpn/choices.py:19 netbox/wireless/choices.py:25 +#: circuits/choices.py:23 core/tables/plugins.py:64 core/tables/tasks.py:22 +#: dcim/choices.py:22 dcim/choices.py:103 dcim/choices.py:185 +#: dcim/choices.py:238 dcim/choices.py:1602 dcim/choices.py:1669 +#: dcim/choices.py:1691 extras/tables/tables.py:539 ipam/choices.py:31 +#: ipam/choices.py:49 ipam/choices.py:69 ipam/choices.py:154 +#: templates/extras/configcontext.html:25 templates/users/user.html:37 +#: users/forms/bulk_edit.py:38 virtualization/choices.py:22 +#: virtualization/choices.py:45 vpn/choices.py:19 vpn/choices.py:280 +#: wireless/choices.py:25 msgid "Active" msgstr "" -#: netbox/circuits/choices.py:24 netbox/dcim/choices.py:183 -#: netbox/dcim/choices.py:235 netbox/dcim/choices.py:1598 -#: netbox/dcim/choices.py:1651 netbox/dcim/choices.py:1670 -#: netbox/virtualization/choices.py:24 netbox/virtualization/choices.py:44 +#: circuits/choices.py:24 dcim/choices.py:184 dcim/choices.py:237 +#: dcim/choices.py:1601 dcim/choices.py:1671 dcim/choices.py:1690 +#: virtualization/choices.py:24 virtualization/choices.py:44 msgid "Offline" msgstr "" -#: netbox/circuits/choices.py:25 +#: circuits/choices.py:25 msgid "Deprovisioning" msgstr "" -#: netbox/circuits/choices.py:26 +#: circuits/choices.py:26 msgid "Decommissioned" msgstr "" -#: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 -#: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:83 -#: netbox/tenancy/choices.py:17 +#: circuits/choices.py:90 dcim/choices.py:1614 +#: templates/dcim/interface.html:135 +#: templates/virtualization/vminterface.html:83 tenancy/choices.py:17 msgid "Primary" msgstr "" -#: netbox/circuits/choices.py:91 netbox/ipam/choices.py:90 -#: netbox/tenancy/choices.py:18 +#: circuits/choices.py:91 ipam/choices.py:90 tenancy/choices.py:18 msgid "Secondary" msgstr "" -#: netbox/circuits/choices.py:92 netbox/tenancy/choices.py:19 +#: circuits/choices.py:92 tenancy/choices.py:19 msgid "Tertiary" msgstr "" -#: netbox/circuits/choices.py:93 netbox/tenancy/choices.py:20 +#: circuits/choices.py:93 tenancy/choices.py:20 msgid "Inactive" msgstr "" -#: netbox/circuits/choices.py:107 netbox/templates/dcim/interface.html:275 -#: netbox/vpn/choices.py:63 +#: circuits/choices.py:107 templates/dcim/interface.html:275 vpn/choices.py:63 msgid "Peer" msgstr "" -#: netbox/circuits/choices.py:108 netbox/vpn/choices.py:64 +#: circuits/choices.py:108 vpn/choices.py:64 msgid "Hub" msgstr "" -#: netbox/circuits/choices.py:109 netbox/vpn/choices.py:65 +#: circuits/choices.py:109 vpn/choices.py:65 msgid "Spoke" msgstr "" -#: netbox/circuits/filtersets.py:37 netbox/circuits/filtersets.py:204 -#: netbox/circuits/filtersets.py:284 netbox/dcim/base_filtersets.py:22 -#: netbox/dcim/filtersets.py:99 netbox/dcim/filtersets.py:153 -#: netbox/dcim/filtersets.py:213 netbox/dcim/filtersets.py:334 -#: netbox/dcim/filtersets.py:465 netbox/dcim/filtersets.py:1022 -#: netbox/dcim/filtersets.py:1344 netbox/dcim/filtersets.py:1442 -#: netbox/dcim/filtersets.py:2103 netbox/dcim/filtersets.py:2346 -#: netbox/dcim/filtersets.py:2404 netbox/ipam/filtersets.py:954 -#: netbox/virtualization/filtersets.py:139 netbox/vpn/filtersets.py:358 +#: circuits/filtersets.py:37 circuits/filtersets.py:204 +#: circuits/filtersets.py:284 dcim/base_filtersets.py:22 dcim/filtersets.py:101 +#: dcim/filtersets.py:155 dcim/filtersets.py:215 dcim/filtersets.py:336 +#: dcim/filtersets.py:467 dcim/filtersets.py:1075 dcim/filtersets.py:1397 +#: dcim/filtersets.py:1495 dcim/filtersets.py:2160 dcim/filtersets.py:2403 +#: dcim/filtersets.py:2461 ipam/filtersets.py:954 +#: virtualization/filtersets.py:139 vpn/filtersets.py:361 msgid "Region (ID)" msgstr "" -#: netbox/circuits/filtersets.py:44 netbox/circuits/filtersets.py:211 -#: netbox/circuits/filtersets.py:291 netbox/dcim/base_filtersets.py:29 -#: netbox/dcim/filtersets.py:106 netbox/dcim/filtersets.py:159 -#: netbox/dcim/filtersets.py:220 netbox/dcim/filtersets.py:341 -#: netbox/dcim/filtersets.py:472 netbox/dcim/filtersets.py:1029 -#: netbox/dcim/filtersets.py:1351 netbox/dcim/filtersets.py:1449 -#: netbox/dcim/filtersets.py:2110 netbox/dcim/filtersets.py:2353 -#: netbox/dcim/filtersets.py:2411 netbox/extras/filtersets.py:509 -#: netbox/ipam/filtersets.py:961 netbox/virtualization/filtersets.py:146 -#: netbox/vpn/filtersets.py:353 +#: circuits/filtersets.py:44 circuits/filtersets.py:211 +#: circuits/filtersets.py:291 dcim/base_filtersets.py:29 dcim/filtersets.py:108 +#: dcim/filtersets.py:161 dcim/filtersets.py:222 dcim/filtersets.py:343 +#: dcim/filtersets.py:474 dcim/filtersets.py:1082 dcim/filtersets.py:1404 +#: dcim/filtersets.py:1502 dcim/filtersets.py:2167 dcim/filtersets.py:2410 +#: dcim/filtersets.py:2468 extras/filtersets.py:602 ipam/filtersets.py:961 +#: virtualization/filtersets.py:146 vpn/filtersets.py:356 msgid "Region (slug)" msgstr "" -#: netbox/circuits/filtersets.py:50 netbox/circuits/filtersets.py:217 -#: netbox/circuits/filtersets.py:297 netbox/dcim/base_filtersets.py:35 -#: netbox/dcim/filtersets.py:129 netbox/dcim/filtersets.py:226 -#: netbox/dcim/filtersets.py:347 netbox/dcim/filtersets.py:478 -#: netbox/dcim/filtersets.py:1035 netbox/dcim/filtersets.py:1357 -#: netbox/dcim/filtersets.py:1455 netbox/dcim/filtersets.py:2116 -#: netbox/dcim/filtersets.py:2359 netbox/dcim/filtersets.py:2417 -#: netbox/ipam/filtersets.py:239 netbox/ipam/filtersets.py:967 -#: netbox/virtualization/filtersets.py:152 +#: circuits/filtersets.py:50 circuits/filtersets.py:217 +#: circuits/filtersets.py:297 dcim/base_filtersets.py:35 dcim/filtersets.py:131 +#: dcim/filtersets.py:228 dcim/filtersets.py:349 dcim/filtersets.py:480 +#: dcim/filtersets.py:1088 dcim/filtersets.py:1410 dcim/filtersets.py:1508 +#: dcim/filtersets.py:2173 dcim/filtersets.py:2416 dcim/filtersets.py:2474 +#: ipam/filtersets.py:239 ipam/filtersets.py:967 +#: virtualization/filtersets.py:152 msgid "Site group (ID)" msgstr "" -#: netbox/circuits/filtersets.py:57 netbox/circuits/filtersets.py:224 -#: netbox/circuits/filtersets.py:304 netbox/dcim/base_filtersets.py:42 -#: netbox/dcim/filtersets.py:136 netbox/dcim/filtersets.py:233 -#: netbox/dcim/filtersets.py:354 netbox/dcim/filtersets.py:485 -#: netbox/dcim/filtersets.py:1042 netbox/dcim/filtersets.py:1364 -#: netbox/dcim/filtersets.py:1462 netbox/dcim/filtersets.py:2123 -#: netbox/dcim/filtersets.py:2366 netbox/dcim/filtersets.py:2424 -#: netbox/extras/filtersets.py:515 netbox/ipam/filtersets.py:246 -#: netbox/ipam/filtersets.py:974 netbox/virtualization/filtersets.py:159 +#: circuits/filtersets.py:57 circuits/filtersets.py:224 +#: circuits/filtersets.py:304 dcim/base_filtersets.py:42 dcim/filtersets.py:138 +#: dcim/filtersets.py:235 dcim/filtersets.py:356 dcim/filtersets.py:487 +#: dcim/filtersets.py:1095 dcim/filtersets.py:1417 dcim/filtersets.py:1515 +#: dcim/filtersets.py:2180 dcim/filtersets.py:2423 dcim/filtersets.py:2481 +#: extras/filtersets.py:608 ipam/filtersets.py:246 ipam/filtersets.py:974 +#: virtualization/filtersets.py:159 msgid "Site group (slug)" msgstr "" -#: netbox/circuits/filtersets.py:62 netbox/circuits/forms/filtersets.py:59 -#: netbox/circuits/forms/filtersets.py:183 -#: netbox/circuits/forms/filtersets.py:241 -#: netbox/circuits/tables/circuits.py:128 netbox/dcim/forms/bulk_edit.py:172 -#: netbox/dcim/forms/bulk_edit.py:333 netbox/dcim/forms/bulk_edit.py:686 -#: netbox/dcim/forms/bulk_edit.py:891 netbox/dcim/forms/bulk_import.py:133 -#: netbox/dcim/forms/bulk_import.py:232 netbox/dcim/forms/bulk_import.py:333 -#: netbox/dcim/forms/bulk_import.py:567 netbox/dcim/forms/bulk_import.py:1448 -#: netbox/dcim/forms/bulk_import.py:1476 netbox/dcim/forms/filtersets.py:88 -#: netbox/dcim/forms/filtersets.py:226 netbox/dcim/forms/filtersets.py:343 -#: netbox/dcim/forms/filtersets.py:440 netbox/dcim/forms/filtersets.py:754 -#: netbox/dcim/forms/filtersets.py:973 netbox/dcim/forms/filtersets.py:1046 -#: netbox/dcim/forms/filtersets.py:1070 netbox/dcim/forms/filtersets.py:1160 -#: netbox/dcim/forms/filtersets.py:1198 netbox/dcim/forms/filtersets.py:1681 -#: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 -#: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 -#: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 -#: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 -#: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 -#: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 -#: netbox/extras/filtersets.py:525 netbox/ipam/forms/bulk_edit.py:468 -#: netbox/ipam/forms/bulk_import.py:468 netbox/ipam/forms/filtersets.py:161 -#: netbox/ipam/forms/filtersets.py:236 netbox/ipam/forms/filtersets.py:449 -#: netbox/ipam/forms/filtersets.py:544 netbox/ipam/forms/model_forms.py:678 -#: netbox/ipam/tables/vlans.py:87 netbox/ipam/tables/vlans.py:197 -#: netbox/templates/dcim/device.html:22 -#: netbox/templates/dcim/inc/cable_termination.html:8 -#: netbox/templates/dcim/inc/cable_termination.html:38 -#: netbox/templates/dcim/location.html:37 -#: netbox/templates/dcim/powerpanel.html:22 netbox/templates/dcim/rack.html:20 -#: netbox/templates/dcim/rackreservation.html:28 -#: netbox/templates/dcim/site.html:28 netbox/templates/ipam/vlan.html:23 -#: netbox/templates/ipam/vlan_edit.html:52 -#: netbox/templates/virtualization/virtualmachine.html:95 -#: netbox/virtualization/forms/bulk_edit.py:106 -#: netbox/virtualization/forms/bulk_import.py:60 -#: netbox/virtualization/forms/bulk_import.py:91 -#: netbox/virtualization/forms/filtersets.py:75 -#: netbox/virtualization/forms/filtersets.py:154 -#: netbox/virtualization/forms/model_forms.py:104 -#: netbox/virtualization/forms/model_forms.py:178 -#: netbox/virtualization/tables/virtualmachines.py:33 -#: netbox/vpn/forms/filtersets.py:272 netbox/wireless/forms/filtersets.py:88 -#: netbox/wireless/forms/model_forms.py:79 -#: netbox/wireless/forms/model_forms.py:121 +#: circuits/filtersets.py:62 circuits/forms/filtersets.py:59 +#: circuits/forms/filtersets.py:183 circuits/forms/filtersets.py:241 +#: circuits/tables/circuits.py:128 dcim/forms/bulk_edit.py:177 +#: dcim/forms/bulk_edit.py:344 dcim/forms/bulk_edit.py:730 +#: dcim/forms/bulk_edit.py:935 dcim/forms/bulk_import.py:134 +#: dcim/forms/bulk_import.py:236 dcim/forms/bulk_import.py:337 +#: dcim/forms/bulk_import.py:598 dcim/forms/bulk_import.py:1479 +#: dcim/forms/bulk_import.py:1507 dcim/forms/filtersets.py:89 +#: dcim/forms/filtersets.py:227 dcim/forms/filtersets.py:344 +#: dcim/forms/filtersets.py:441 dcim/forms/filtersets.py:773 +#: dcim/forms/filtersets.py:992 dcim/forms/filtersets.py:1065 +#: dcim/forms/filtersets.py:1089 dcim/forms/filtersets.py:1179 +#: dcim/forms/filtersets.py:1217 dcim/forms/filtersets.py:1705 +#: dcim/forms/filtersets.py:1729 dcim/forms/filtersets.py:1753 +#: dcim/forms/model_forms.py:146 dcim/forms/model_forms.py:174 +#: dcim/forms/model_forms.py:250 dcim/forms/model_forms.py:567 +#: dcim/forms/model_forms.py:828 dcim/forms/object_create.py:395 +#: dcim/tables/devices.py:163 dcim/tables/power.py:26 dcim/tables/power.py:93 +#: dcim/tables/racks.py:125 dcim/tables/racks.py:215 dcim/tables/sites.py:139 +#: extras/filtersets.py:618 ipam/forms/bulk_edit.py:479 +#: ipam/forms/bulk_import.py:475 ipam/forms/filtersets.py:161 +#: ipam/forms/filtersets.py:236 ipam/forms/filtersets.py:457 +#: ipam/forms/filtersets.py:552 ipam/forms/model_forms.py:679 +#: ipam/tables/vlans.py:89 ipam/tables/vlans.py:199 +#: templates/dcim/device.html:22 templates/dcim/inc/cable_termination.html:8 +#: templates/dcim/inc/cable_termination.html:38 templates/dcim/location.html:37 +#: templates/dcim/powerpanel.html:22 templates/dcim/rack.html:20 +#: templates/dcim/rackreservation.html:28 templates/dcim/site.html:28 +#: templates/ipam/vlan.html:23 templates/ipam/vlan_edit.html:52 +#: templates/virtualization/virtualmachine.html:95 +#: virtualization/forms/bulk_edit.py:106 virtualization/forms/bulk_import.py:60 +#: virtualization/forms/bulk_import.py:91 virtualization/forms/filtersets.py:75 +#: virtualization/forms/filtersets.py:154 +#: virtualization/forms/model_forms.py:104 +#: virtualization/forms/model_forms.py:178 +#: virtualization/tables/virtualmachines.py:33 vpn/forms/filtersets.py:277 +#: wireless/forms/filtersets.py:88 wireless/forms/model_forms.py:80 +#: wireless/forms/model_forms.py:122 msgid "Site" msgstr "" -#: netbox/circuits/filtersets.py:68 netbox/circuits/filtersets.py:235 -#: netbox/circuits/filtersets.py:315 netbox/dcim/base_filtersets.py:53 -#: netbox/dcim/filtersets.py:243 netbox/dcim/filtersets.py:364 -#: netbox/dcim/filtersets.py:459 netbox/extras/filtersets.py:531 -#: netbox/ipam/filtersets.py:257 netbox/ipam/filtersets.py:984 -#: netbox/virtualization/filtersets.py:169 netbox/vpn/filtersets.py:363 +#: circuits/filtersets.py:68 circuits/filtersets.py:235 +#: circuits/filtersets.py:315 dcim/base_filtersets.py:53 dcim/filtersets.py:245 +#: dcim/filtersets.py:366 dcim/filtersets.py:461 extras/filtersets.py:624 +#: ipam/filtersets.py:257 ipam/filtersets.py:984 +#: virtualization/filtersets.py:169 vpn/filtersets.py:366 msgid "Site (slug)" msgstr "" -#: netbox/circuits/filtersets.py:73 +#: circuits/filtersets.py:73 msgid "ASN (ID)" msgstr "" -#: netbox/circuits/filtersets.py:79 netbox/circuits/forms/filtersets.py:39 -#: netbox/ipam/forms/model_forms.py:165 netbox/ipam/models/asns.py:105 -#: netbox/ipam/models/asns.py:122 netbox/ipam/tables/asn.py:41 -#: netbox/templates/ipam/asn.html:20 +#: circuits/filtersets.py:79 circuits/forms/filtersets.py:39 +#: ipam/forms/model_forms.py:165 ipam/models/asns.py:105 +#: ipam/models/asns.py:122 ipam/tables/asn.py:41 templates/ipam/asn.html:20 msgid "ASN" msgstr "" -#: netbox/circuits/filtersets.py:101 netbox/circuits/filtersets.py:128 -#: netbox/circuits/filtersets.py:162 netbox/circuits/filtersets.py:338 -#: netbox/circuits/filtersets.py:406 netbox/circuits/filtersets.py:482 -#: netbox/circuits/filtersets.py:550 netbox/ipam/filtersets.py:262 +#: circuits/filtersets.py:101 circuits/filtersets.py:128 +#: circuits/filtersets.py:162 circuits/filtersets.py:338 +#: circuits/filtersets.py:406 circuits/filtersets.py:482 +#: circuits/filtersets.py:550 ipam/filtersets.py:262 msgid "Provider (ID)" msgstr "" -#: netbox/circuits/filtersets.py:107 netbox/circuits/filtersets.py:134 -#: netbox/circuits/filtersets.py:168 netbox/circuits/filtersets.py:344 -#: netbox/circuits/filtersets.py:488 netbox/circuits/filtersets.py:556 -#: netbox/ipam/filtersets.py:268 +#: circuits/filtersets.py:107 circuits/filtersets.py:134 +#: circuits/filtersets.py:168 circuits/filtersets.py:344 +#: circuits/filtersets.py:488 circuits/filtersets.py:556 ipam/filtersets.py:268 msgid "Provider (slug)" msgstr "" -#: netbox/circuits/filtersets.py:173 netbox/circuits/filtersets.py:493 -#: netbox/circuits/filtersets.py:561 +#: circuits/filtersets.py:173 circuits/filtersets.py:493 +#: circuits/filtersets.py:561 msgid "Provider account (ID)" msgstr "" -#: netbox/circuits/filtersets.py:179 netbox/circuits/filtersets.py:499 -#: netbox/circuits/filtersets.py:567 +#: circuits/filtersets.py:179 circuits/filtersets.py:499 +#: circuits/filtersets.py:567 msgid "Provider account (account)" msgstr "" -#: netbox/circuits/filtersets.py:184 netbox/circuits/filtersets.py:503 -#: netbox/circuits/filtersets.py:572 +#: circuits/filtersets.py:184 circuits/filtersets.py:503 +#: circuits/filtersets.py:572 msgid "Provider network (ID)" msgstr "" -#: netbox/circuits/filtersets.py:188 +#: circuits/filtersets.py:188 msgid "Circuit type (ID)" msgstr "" -#: netbox/circuits/filtersets.py:194 +#: circuits/filtersets.py:194 msgid "Circuit type (slug)" msgstr "" -#: netbox/circuits/filtersets.py:229 netbox/circuits/filtersets.py:309 -#: netbox/dcim/base_filtersets.py:47 netbox/dcim/filtersets.py:237 -#: netbox/dcim/filtersets.py:358 netbox/dcim/filtersets.py:453 -#: netbox/dcim/filtersets.py:1046 netbox/dcim/filtersets.py:1369 -#: netbox/dcim/filtersets.py:1467 netbox/dcim/filtersets.py:2128 -#: netbox/dcim/filtersets.py:2370 netbox/dcim/filtersets.py:2429 -#: netbox/ipam/filtersets.py:251 netbox/ipam/filtersets.py:978 -#: netbox/virtualization/filtersets.py:163 netbox/vpn/filtersets.py:368 +#: circuits/filtersets.py:229 circuits/filtersets.py:309 +#: dcim/base_filtersets.py:47 dcim/filtersets.py:239 dcim/filtersets.py:360 +#: dcim/filtersets.py:455 dcim/filtersets.py:1099 dcim/filtersets.py:1422 +#: dcim/filtersets.py:1520 dcim/filtersets.py:2185 dcim/filtersets.py:2427 +#: dcim/filtersets.py:2486 ipam/filtersets.py:251 ipam/filtersets.py:978 +#: virtualization/filtersets.py:163 vpn/filtersets.py:371 msgid "Site (ID)" msgstr "" -#: netbox/circuits/filtersets.py:239 netbox/circuits/filtersets.py:321 -#: netbox/dcim/base_filtersets.py:59 netbox/dcim/filtersets.py:259 -#: netbox/dcim/filtersets.py:370 netbox/dcim/filtersets.py:491 -#: netbox/dcim/filtersets.py:1058 netbox/dcim/filtersets.py:1380 -#: netbox/dcim/filtersets.py:1478 netbox/dcim/filtersets.py:2382 +#: circuits/filtersets.py:239 circuits/filtersets.py:321 +#: dcim/base_filtersets.py:59 dcim/filtersets.py:261 dcim/filtersets.py:372 +#: dcim/filtersets.py:493 dcim/filtersets.py:1111 dcim/filtersets.py:1433 +#: dcim/filtersets.py:1531 dcim/filtersets.py:2439 msgid "Location (ID)" msgstr "" -#: netbox/circuits/filtersets.py:244 netbox/circuits/filtersets.py:248 +#: circuits/filtersets.py:244 circuits/filtersets.py:248 msgid "Termination A (ID)" msgstr "" -#: netbox/circuits/filtersets.py:273 netbox/circuits/filtersets.py:375 -#: netbox/circuits/filtersets.py:537 netbox/core/filtersets.py:77 -#: netbox/core/filtersets.py:136 netbox/core/filtersets.py:173 -#: netbox/dcim/filtersets.py:752 netbox/dcim/filtersets.py:1436 -#: netbox/dcim/filtersets.py:2477 netbox/extras/filtersets.py:41 -#: netbox/extras/filtersets.py:63 netbox/extras/filtersets.py:92 -#: netbox/extras/filtersets.py:132 netbox/extras/filtersets.py:181 -#: netbox/extras/filtersets.py:209 netbox/extras/filtersets.py:239 -#: netbox/extras/filtersets.py:276 netbox/extras/filtersets.py:348 -#: netbox/extras/filtersets.py:391 netbox/extras/filtersets.py:438 -#: netbox/extras/filtersets.py:498 netbox/extras/filtersets.py:657 -#: netbox/extras/filtersets.py:703 netbox/ipam/forms/model_forms.py:492 -#: netbox/netbox/filtersets.py:286 netbox/netbox/forms/__init__.py:22 -#: netbox/netbox/forms/base.py:167 -#: netbox/templates/htmx/object_selector.html:28 -#: netbox/templates/inc/filter_list.html:46 -#: netbox/templates/ipam/ipaddress_assign.html:29 -#: netbox/templates/search.html:7 netbox/templates/search.html:26 -#: netbox/tenancy/filtersets.py:99 netbox/users/filtersets.py:23 -#: netbox/users/filtersets.py:57 netbox/users/filtersets.py:102 -#: netbox/users/filtersets.py:150 netbox/utilities/forms/forms.py:104 -#: netbox/utilities/templates/navigation/menu.html:16 +#: circuits/filtersets.py:273 circuits/filtersets.py:375 +#: circuits/filtersets.py:537 core/filtersets.py:81 core/filtersets.py:140 +#: core/filtersets.py:177 dcim/filtersets.py:780 dcim/filtersets.py:1489 +#: dcim/filtersets.py:2534 extras/filtersets.py:45 extras/filtersets.py:67 +#: extras/filtersets.py:96 extras/filtersets.py:136 extras/filtersets.py:185 +#: extras/filtersets.py:213 extras/filtersets.py:243 extras/filtersets.py:281 +#: extras/filtersets.py:333 extras/filtersets.py:406 extras/filtersets.py:449 +#: extras/filtersets.py:496 extras/filtersets.py:556 extras/filtersets.py:591 +#: extras/filtersets.py:750 extras/filtersets.py:800 +#: ipam/forms/model_forms.py:492 netbox/filtersets.py:289 +#: netbox/forms/__init__.py:22 netbox/forms/base.py:167 +#: templates/htmx/object_selector.html:28 templates/inc/filter_list.html:46 +#: templates/ipam/ipaddress_assign.html:29 templates/search.html:7 +#: templates/search.html:26 tenancy/filtersets.py:104 users/filtersets.py:23 +#: users/filtersets.py:57 users/filtersets.py:102 users/filtersets.py:150 +#: utilities/forms/forms.py:104 utilities/templates/navigation/menu.html:16 msgid "Search" msgstr "" -#: netbox/circuits/filtersets.py:277 netbox/circuits/forms/bulk_edit.py:195 -#: netbox/circuits/forms/bulk_edit.py:284 -#: netbox/circuits/forms/bulk_import.py:128 -#: netbox/circuits/forms/filtersets.py:224 -#: netbox/circuits/forms/filtersets.py:251 -#: netbox/circuits/forms/filtersets.py:297 -#: netbox/circuits/forms/model_forms.py:139 -#: netbox/circuits/forms/model_forms.py:162 -#: netbox/circuits/forms/model_forms.py:262 -#: netbox/circuits/tables/circuits.py:107 -#: netbox/circuits/tables/circuits.py:202 netbox/dcim/forms/connections.py:73 -#: netbox/templates/circuits/circuit.html:15 -#: netbox/templates/circuits/circuitgroupassignment.html:30 -#: netbox/templates/circuits/circuittermination.html:19 -#: netbox/templates/dcim/inc/cable_termination.html:66 -#: netbox/templates/dcim/trace/circuit.html:4 +#: circuits/filtersets.py:277 circuits/forms/bulk_edit.py:195 +#: circuits/forms/bulk_edit.py:284 circuits/forms/bulk_import.py:128 +#: circuits/forms/filtersets.py:224 circuits/forms/filtersets.py:251 +#: circuits/forms/filtersets.py:297 circuits/forms/model_forms.py:139 +#: circuits/forms/model_forms.py:162 circuits/forms/model_forms.py:262 +#: circuits/tables/circuits.py:107 circuits/tables/circuits.py:202 +#: dcim/forms/connections.py:73 templates/circuits/circuit.html:15 +#: templates/circuits/circuitgroupassignment.html:30 +#: templates/circuits/circuittermination.html:19 +#: templates/dcim/inc/cable_termination.html:66 +#: templates/dcim/trace/circuit.html:4 msgid "Circuit" msgstr "" -#: netbox/circuits/filtersets.py:328 netbox/dcim/base_filtersets.py:66 -#: netbox/dcim/filtersets.py:266 netbox/dcim/filtersets.py:377 -#: netbox/dcim/filtersets.py:498 netbox/dcim/filtersets.py:1065 -#: netbox/dcim/filtersets.py:1386 netbox/dcim/filtersets.py:1484 -#: netbox/extras/filtersets.py:542 +#: circuits/filtersets.py:328 dcim/base_filtersets.py:66 dcim/filtersets.py:268 +#: dcim/filtersets.py:379 dcim/filtersets.py:500 dcim/filtersets.py:1118 +#: dcim/filtersets.py:1439 dcim/filtersets.py:1537 extras/filtersets.py:635 msgid "Location (slug)" msgstr "" -#: netbox/circuits/filtersets.py:333 +#: circuits/filtersets.py:333 msgid "ProviderNetwork (ID)" msgstr "" -#: netbox/circuits/filtersets.py:381 +#: circuits/filtersets.py:381 msgid "Circuit (CID)" msgstr "" -#: netbox/circuits/filtersets.py:386 +#: circuits/filtersets.py:386 msgid "Circuit (ID)" msgstr "" -#: netbox/circuits/filtersets.py:391 +#: circuits/filtersets.py:391 msgid "Virtual circuit (CID)" msgstr "" -#: netbox/circuits/filtersets.py:396 netbox/dcim/filtersets.py:1925 +#: circuits/filtersets.py:396 dcim/filtersets.py:1982 msgid "Virtual circuit (ID)" msgstr "" -#: netbox/circuits/filtersets.py:401 +#: circuits/filtersets.py:401 msgid "Provider (name)" msgstr "" -#: netbox/circuits/filtersets.py:410 +#: circuits/filtersets.py:410 msgid "Circuit group (ID)" msgstr "" -#: netbox/circuits/filtersets.py:416 +#: circuits/filtersets.py:416 msgid "Circuit group (slug)" msgstr "" -#: netbox/circuits/filtersets.py:507 +#: circuits/filtersets.py:507 msgid "Virtual circuit type (ID)" msgstr "" -#: netbox/circuits/filtersets.py:513 +#: circuits/filtersets.py:513 msgid "Virtual circuit type (slug)" msgstr "" -#: netbox/circuits/filtersets.py:541 netbox/circuits/forms/bulk_edit.py:355 -#: netbox/circuits/forms/bulk_import.py:249 -#: netbox/circuits/forms/filtersets.py:373 -#: netbox/circuits/forms/filtersets.py:379 -#: netbox/circuits/forms/model_forms.py:343 -#: netbox/circuits/forms/model_forms.py:358 -#: netbox/circuits/tables/virtual_circuits.py:88 -#: netbox/templates/circuits/virtualcircuit.html:20 -#: netbox/templates/circuits/virtualcircuittermination.html:38 +#: circuits/filtersets.py:541 circuits/forms/bulk_edit.py:355 +#: circuits/forms/bulk_import.py:249 circuits/forms/filtersets.py:373 +#: circuits/forms/filtersets.py:379 circuits/forms/model_forms.py:343 +#: circuits/forms/model_forms.py:358 circuits/tables/virtual_circuits.py:88 +#: templates/circuits/virtualcircuit.html:20 +#: templates/circuits/virtualcircuittermination.html:38 msgid "Virtual circuit" msgstr "" -#: netbox/circuits/filtersets.py:577 netbox/dcim/filtersets.py:1276 -#: netbox/dcim/filtersets.py:1706 netbox/ipam/filtersets.py:627 -#: netbox/vpn/filtersets.py:102 netbox/vpn/filtersets.py:401 +#: circuits/filtersets.py:577 dcim/filtersets.py:1329 dcim/filtersets.py:1763 +#: ipam/filtersets.py:627 vpn/filtersets.py:102 vpn/filtersets.py:404 msgid "Interface (ID)" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:42 netbox/circuits/forms/filtersets.py:64 -#: netbox/circuits/forms/model_forms.py:42 -#: netbox/circuits/tables/providers.py:32 netbox/dcim/forms/bulk_edit.py:132 -#: netbox/dcim/forms/filtersets.py:196 netbox/dcim/forms/model_forms.py:127 -#: netbox/dcim/tables/sites.py:94 netbox/ipam/models/asns.py:123 -#: netbox/ipam/tables/asn.py:27 netbox/ipam/views.py:230 -#: netbox/netbox/navigation/menu.py:178 netbox/netbox/navigation/menu.py:181 -#: netbox/templates/circuits/provider.html:23 +#: circuits/forms/bulk_edit.py:42 circuits/forms/filtersets.py:64 +#: circuits/forms/model_forms.py:42 circuits/tables/providers.py:32 +#: dcim/forms/bulk_edit.py:137 dcim/forms/filtersets.py:197 +#: dcim/forms/model_forms.py:132 dcim/tables/sites.py:100 +#: ipam/models/asns.py:123 ipam/tables/asn.py:27 ipam/views.py:229 +#: netbox/navigation/menu.py:179 netbox/navigation/menu.py:182 +#: templates/circuits/provider.html:23 msgid "ASNs" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:46 netbox/circuits/forms/bulk_edit.py:68 -#: netbox/circuits/forms/bulk_edit.py:95 netbox/circuits/forms/bulk_edit.py:116 -#: netbox/circuits/forms/bulk_edit.py:187 -#: netbox/circuits/forms/bulk_edit.py:207 -#: netbox/circuits/forms/bulk_edit.py:266 -#: netbox/circuits/forms/bulk_edit.py:307 -#: netbox/circuits/forms/bulk_edit.py:347 -#: netbox/circuits/forms/bulk_edit.py:371 netbox/core/forms/bulk_edit.py:28 -#: netbox/dcim/forms/bulk_create.py:35 netbox/dcim/forms/bulk_edit.py:77 -#: netbox/dcim/forms/bulk_edit.py:96 netbox/dcim/forms/bulk_edit.py:155 -#: netbox/dcim/forms/bulk_edit.py:196 netbox/dcim/forms/bulk_edit.py:214 -#: netbox/dcim/forms/bulk_edit.py:292 netbox/dcim/forms/bulk_edit.py:441 -#: netbox/dcim/forms/bulk_edit.py:475 netbox/dcim/forms/bulk_edit.py:490 -#: netbox/dcim/forms/bulk_edit.py:549 netbox/dcim/forms/bulk_edit.py:593 -#: netbox/dcim/forms/bulk_edit.py:627 netbox/dcim/forms/bulk_edit.py:651 -#: netbox/dcim/forms/bulk_edit.py:724 netbox/dcim/forms/bulk_edit.py:785 -#: netbox/dcim/forms/bulk_edit.py:837 netbox/dcim/forms/bulk_edit.py:860 -#: netbox/dcim/forms/bulk_edit.py:908 netbox/dcim/forms/bulk_edit.py:978 -#: netbox/dcim/forms/bulk_edit.py:1031 netbox/dcim/forms/bulk_edit.py:1066 -#: netbox/dcim/forms/bulk_edit.py:1106 netbox/dcim/forms/bulk_edit.py:1150 -#: netbox/dcim/forms/bulk_edit.py:1195 netbox/dcim/forms/bulk_edit.py:1222 -#: netbox/dcim/forms/bulk_edit.py:1240 netbox/dcim/forms/bulk_edit.py:1258 -#: netbox/dcim/forms/bulk_edit.py:1276 netbox/dcim/forms/bulk_edit.py:1746 -#: netbox/dcim/forms/bulk_edit.py:1787 netbox/extras/forms/bulk_edit.py:39 -#: netbox/extras/forms/bulk_edit.py:149 netbox/extras/forms/bulk_edit.py:178 -#: netbox/extras/forms/bulk_edit.py:208 netbox/extras/forms/bulk_edit.py:256 -#: netbox/extras/forms/bulk_edit.py:274 netbox/extras/forms/bulk_edit.py:298 -#: netbox/extras/forms/bulk_edit.py:312 netbox/extras/forms/bulk_edit.py:339 -#: netbox/extras/tables/tables.py:82 netbox/ipam/forms/bulk_edit.py:56 -#: netbox/ipam/forms/bulk_edit.py:76 netbox/ipam/forms/bulk_edit.py:96 -#: netbox/ipam/forms/bulk_edit.py:120 netbox/ipam/forms/bulk_edit.py:149 -#: netbox/ipam/forms/bulk_edit.py:178 netbox/ipam/forms/bulk_edit.py:197 -#: netbox/ipam/forms/bulk_edit.py:260 netbox/ipam/forms/bulk_edit.py:305 -#: netbox/ipam/forms/bulk_edit.py:353 netbox/ipam/forms/bulk_edit.py:396 -#: netbox/ipam/forms/bulk_edit.py:412 netbox/ipam/forms/bulk_edit.py:500 -#: netbox/ipam/forms/bulk_edit.py:532 netbox/ipam/forms/bulk_edit.py:575 -#: netbox/ipam/tables/vlans.py:240 netbox/ipam/tables/vlans.py:267 -#: netbox/templates/account/token.html:35 -#: netbox/templates/circuits/circuit.html:69 -#: netbox/templates/circuits/circuitgroup.html:32 -#: netbox/templates/circuits/circuittype.html:26 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:83 -#: netbox/templates/circuits/provider.html:33 -#: netbox/templates/circuits/providernetwork.html:32 -#: netbox/templates/circuits/virtualcircuit.html:56 -#: netbox/templates/circuits/virtualcircuittermination.html:68 -#: netbox/templates/circuits/virtualcircuittype.html:26 -#: netbox/templates/core/datasource.html:54 -#: netbox/templates/core/plugin.html:80 netbox/templates/dcim/cable.html:36 -#: netbox/templates/dcim/consoleport.html:44 -#: netbox/templates/dcim/consoleserverport.html:44 -#: netbox/templates/dcim/device.html:94 netbox/templates/dcim/devicebay.html:32 -#: netbox/templates/dcim/devicerole.html:30 -#: netbox/templates/dcim/devicetype.html:33 -#: netbox/templates/dcim/frontport.html:58 -#: netbox/templates/dcim/interface.html:69 -#: netbox/templates/dcim/inventoryitem.html:64 -#: netbox/templates/dcim/inventoryitemrole.html:22 -#: netbox/templates/dcim/location.html:33 -#: netbox/templates/dcim/macaddress.html:21 -#: netbox/templates/dcim/manufacturer.html:40 -#: netbox/templates/dcim/module.html:73 netbox/templates/dcim/modulebay.html:42 -#: netbox/templates/dcim/moduletype.html:39 -#: netbox/templates/dcim/platform.html:33 -#: netbox/templates/dcim/powerfeed.html:40 -#: netbox/templates/dcim/poweroutlet.html:40 -#: netbox/templates/dcim/powerpanel.html:30 -#: netbox/templates/dcim/powerport.html:40 netbox/templates/dcim/rack.html:53 -#: netbox/templates/dcim/rackreservation.html:62 -#: netbox/templates/dcim/rackrole.html:26 -#: netbox/templates/dcim/racktype.html:24 -#: netbox/templates/dcim/rearport.html:54 netbox/templates/dcim/region.html:33 -#: netbox/templates/dcim/site.html:60 netbox/templates/dcim/sitegroup.html:33 -#: netbox/templates/dcim/virtualchassis.html:31 -#: netbox/templates/extras/configcontext.html:21 -#: netbox/templates/extras/configtemplate.html:17 -#: netbox/templates/extras/customfield.html:34 -#: netbox/templates/extras/dashboard/widget_add.html:14 -#: netbox/templates/extras/eventrule.html:21 -#: netbox/templates/extras/exporttemplate.html:19 -#: netbox/templates/extras/notificationgroup.html:20 -#: netbox/templates/extras/savedfilter.html:17 -#: netbox/templates/extras/script_list.html:46 -#: netbox/templates/extras/tag.html:20 netbox/templates/extras/webhook.html:17 -#: netbox/templates/generic/bulk_import.html:120 -#: netbox/templates/ipam/aggregate.html:43 netbox/templates/ipam/asn.html:42 -#: netbox/templates/ipam/asnrange.html:38 -#: netbox/templates/ipam/fhrpgroup.html:34 -#: netbox/templates/ipam/ipaddress.html:55 -#: netbox/templates/ipam/iprange.html:67 netbox/templates/ipam/prefix.html:77 -#: netbox/templates/ipam/rir.html:26 netbox/templates/ipam/role.html:26 -#: netbox/templates/ipam/routetarget.html:21 -#: netbox/templates/ipam/service.html:50 -#: netbox/templates/ipam/servicetemplate.html:27 -#: netbox/templates/ipam/vlan.html:62 netbox/templates/ipam/vlangroup.html:34 -#: netbox/templates/ipam/vlantranslationpolicy.html:18 -#: netbox/templates/ipam/vlantranslationrule.html:26 -#: netbox/templates/ipam/vrf.html:33 netbox/templates/tenancy/contact.html:67 -#: netbox/templates/tenancy/contactgroup.html:25 -#: netbox/templates/tenancy/contactrole.html:22 -#: netbox/templates/tenancy/tenant.html:24 -#: netbox/templates/tenancy/tenantgroup.html:33 -#: netbox/templates/users/group.html:21 -#: netbox/templates/users/objectpermission.html:21 -#: netbox/templates/users/token.html:27 -#: netbox/templates/virtualization/cluster.html:25 -#: netbox/templates/virtualization/clustergroup.html:26 -#: netbox/templates/virtualization/clustertype.html:26 -#: netbox/templates/virtualization/virtualdisk.html:39 -#: netbox/templates/virtualization/virtualmachine.html:31 -#: netbox/templates/virtualization/vminterface.html:47 -#: netbox/templates/vpn/ikepolicy.html:17 -#: netbox/templates/vpn/ikeproposal.html:17 -#: netbox/templates/vpn/ipsecpolicy.html:17 -#: netbox/templates/vpn/ipsecprofile.html:17 -#: netbox/templates/vpn/ipsecprofile.html:40 -#: netbox/templates/vpn/ipsecprofile.html:73 -#: netbox/templates/vpn/ipsecproposal.html:17 -#: netbox/templates/vpn/l2vpn.html:26 netbox/templates/vpn/tunnel.html:33 -#: netbox/templates/vpn/tunnelgroup.html:30 -#: netbox/templates/wireless/wirelesslan.html:34 -#: netbox/templates/wireless/wirelesslangroup.html:33 -#: netbox/templates/wireless/wirelesslink.html:34 -#: netbox/tenancy/forms/bulk_edit.py:32 netbox/tenancy/forms/bulk_edit.py:80 -#: netbox/tenancy/forms/bulk_edit.py:123 netbox/users/forms/bulk_edit.py:64 -#: netbox/users/forms/bulk_edit.py:82 netbox/users/forms/bulk_edit.py:112 -#: netbox/virtualization/forms/bulk_edit.py:33 -#: netbox/virtualization/forms/bulk_edit.py:47 -#: netbox/virtualization/forms/bulk_edit.py:82 -#: netbox/virtualization/forms/bulk_edit.py:159 -#: netbox/virtualization/forms/bulk_edit.py:210 -#: netbox/virtualization/forms/bulk_edit.py:327 -#: netbox/vpn/forms/bulk_edit.py:28 netbox/vpn/forms/bulk_edit.py:64 -#: netbox/vpn/forms/bulk_edit.py:121 netbox/vpn/forms/bulk_edit.py:155 -#: netbox/vpn/forms/bulk_edit.py:190 netbox/vpn/forms/bulk_edit.py:215 -#: netbox/vpn/forms/bulk_edit.py:247 netbox/vpn/forms/bulk_edit.py:274 -#: netbox/wireless/forms/bulk_edit.py:31 netbox/wireless/forms/bulk_edit.py:84 -#: netbox/wireless/forms/bulk_edit.py:143 +#: circuits/forms/bulk_edit.py:46 circuits/forms/bulk_edit.py:68 +#: circuits/forms/bulk_edit.py:95 circuits/forms/bulk_edit.py:116 +#: circuits/forms/bulk_edit.py:187 circuits/forms/bulk_edit.py:207 +#: circuits/forms/bulk_edit.py:266 circuits/forms/bulk_edit.py:307 +#: circuits/forms/bulk_edit.py:347 circuits/forms/bulk_edit.py:371 +#: core/forms/bulk_edit.py:29 dcim/forms/bulk_create.py:35 +#: dcim/forms/bulk_edit.py:80 dcim/forms/bulk_edit.py:100 +#: dcim/forms/bulk_edit.py:160 dcim/forms/bulk_edit.py:201 +#: dcim/forms/bulk_edit.py:220 dcim/forms/bulk_edit.py:303 +#: dcim/forms/bulk_edit.py:457 dcim/forms/bulk_edit.py:489 +#: dcim/forms/bulk_edit.py:504 dcim/forms/bulk_edit.py:563 +#: dcim/forms/bulk_edit.py:586 dcim/forms/bulk_edit.py:631 +#: dcim/forms/bulk_edit.py:670 dcim/forms/bulk_edit.py:695 +#: dcim/forms/bulk_edit.py:768 dcim/forms/bulk_edit.py:829 +#: dcim/forms/bulk_edit.py:881 dcim/forms/bulk_edit.py:904 +#: dcim/forms/bulk_edit.py:952 dcim/forms/bulk_edit.py:1022 +#: dcim/forms/bulk_edit.py:1075 dcim/forms/bulk_edit.py:1110 +#: dcim/forms/bulk_edit.py:1150 dcim/forms/bulk_edit.py:1194 +#: dcim/forms/bulk_edit.py:1239 dcim/forms/bulk_edit.py:1266 +#: dcim/forms/bulk_edit.py:1284 dcim/forms/bulk_edit.py:1302 +#: dcim/forms/bulk_edit.py:1320 dcim/forms/bulk_edit.py:1793 +#: dcim/forms/bulk_edit.py:1834 extras/forms/bulk_edit.py:40 +#: extras/forms/bulk_edit.py:150 extras/forms/bulk_edit.py:183 +#: extras/forms/bulk_edit.py:211 extras/forms/bulk_edit.py:241 +#: extras/forms/bulk_edit.py:289 extras/forms/bulk_edit.py:307 +#: extras/forms/bulk_edit.py:335 extras/forms/bulk_edit.py:349 +#: extras/forms/bulk_edit.py:395 extras/tables/tables.py:83 +#: ipam/forms/bulk_edit.py:56 ipam/forms/bulk_edit.py:76 +#: ipam/forms/bulk_edit.py:96 ipam/forms/bulk_edit.py:120 +#: ipam/forms/bulk_edit.py:149 ipam/forms/bulk_edit.py:178 +#: ipam/forms/bulk_edit.py:197 ipam/forms/bulk_edit.py:260 +#: ipam/forms/bulk_edit.py:310 ipam/forms/bulk_edit.py:358 +#: ipam/forms/bulk_edit.py:401 ipam/forms/bulk_edit.py:417 +#: ipam/forms/bulk_edit.py:511 ipam/forms/bulk_edit.py:543 +#: ipam/forms/bulk_edit.py:586 ipam/tables/vlans.py:242 +#: ipam/tables/vlans.py:269 templates/account/token.html:35 +#: templates/circuits/circuit.html:69 templates/circuits/circuitgroup.html:32 +#: templates/circuits/circuittype.html:26 +#: templates/circuits/inc/circuit_termination_fields.html:83 +#: templates/circuits/provider.html:33 +#: templates/circuits/providernetwork.html:32 +#: templates/circuits/virtualcircuit.html:56 +#: templates/circuits/virtualcircuittermination.html:68 +#: templates/circuits/virtualcircuittype.html:26 +#: templates/core/datasource.html:58 templates/core/plugin.html:80 +#: templates/dcim/cable.html:36 templates/dcim/consoleport.html:44 +#: templates/dcim/consoleserverport.html:44 templates/dcim/device.html:94 +#: templates/dcim/devicebay.html:32 templates/dcim/devicerole.html:30 +#: templates/dcim/devicetype.html:33 templates/dcim/frontport.html:58 +#: templates/dcim/interface.html:69 templates/dcim/inventoryitem.html:64 +#: templates/dcim/inventoryitemrole.html:22 templates/dcim/location.html:33 +#: templates/dcim/macaddress.html:21 templates/dcim/manufacturer.html:40 +#: templates/dcim/module.html:73 templates/dcim/modulebay.html:42 +#: templates/dcim/moduletype.html:43 templates/dcim/moduletypeprofile.html:20 +#: templates/dcim/platform.html:33 templates/dcim/powerfeed.html:40 +#: templates/dcim/poweroutlet.html:44 templates/dcim/powerpanel.html:30 +#: templates/dcim/powerport.html:40 templates/dcim/rack.html:53 +#: templates/dcim/rackreservation.html:62 templates/dcim/rackrole.html:26 +#: templates/dcim/racktype.html:24 templates/dcim/rearport.html:54 +#: templates/dcim/region.html:33 templates/dcim/site.html:60 +#: templates/dcim/sitegroup.html:33 templates/dcim/virtualchassis.html:31 +#: templates/extras/configcontext.html:21 +#: templates/extras/configtemplate.html:17 templates/extras/customfield.html:34 +#: templates/extras/dashboard/widget_add.html:14 +#: templates/extras/eventrule.html:21 templates/extras/exporttemplate.html:24 +#: templates/extras/notificationgroup.html:20 +#: templates/extras/savedfilter.html:17 templates/extras/script_list.html:46 +#: templates/extras/tableconfig.html:17 templates/extras/tag.html:20 +#: templates/extras/webhook.html:17 templates/generic/bulk_import.html:120 +#: templates/ipam/aggregate.html:43 templates/ipam/asn.html:42 +#: templates/ipam/asnrange.html:38 templates/ipam/fhrpgroup.html:34 +#: templates/ipam/ipaddress.html:55 templates/ipam/iprange.html:74 +#: templates/ipam/prefix.html:77 templates/ipam/rir.html:26 +#: templates/ipam/role.html:26 templates/ipam/routetarget.html:21 +#: templates/ipam/service.html:52 templates/ipam/servicetemplate.html:27 +#: templates/ipam/vlan.html:62 templates/ipam/vlangroup.html:34 +#: templates/ipam/vlantranslationpolicy.html:18 +#: templates/ipam/vlantranslationrule.html:26 templates/ipam/vrf.html:33 +#: templates/tenancy/contact.html:77 templates/tenancy/contactgroup.html:25 +#: templates/tenancy/contactrole.html:22 templates/tenancy/tenant.html:24 +#: templates/tenancy/tenantgroup.html:33 templates/users/group.html:21 +#: templates/users/objectpermission.html:21 templates/users/token.html:27 +#: templates/virtualization/cluster.html:25 +#: templates/virtualization/clustergroup.html:26 +#: templates/virtualization/clustertype.html:26 +#: templates/virtualization/virtualdisk.html:39 +#: templates/virtualization/virtualmachine.html:31 +#: templates/virtualization/vminterface.html:47 templates/vpn/ikepolicy.html:17 +#: templates/vpn/ikeproposal.html:17 templates/vpn/ipsecpolicy.html:17 +#: templates/vpn/ipsecprofile.html:17 templates/vpn/ipsecprofile.html:40 +#: templates/vpn/ipsecprofile.html:73 templates/vpn/ipsecproposal.html:17 +#: templates/vpn/l2vpn.html:30 templates/vpn/tunnel.html:33 +#: templates/vpn/tunnelgroup.html:30 templates/wireless/wirelesslan.html:34 +#: templates/wireless/wirelesslangroup.html:33 +#: templates/wireless/wirelesslink.html:34 tenancy/forms/bulk_edit.py:32 +#: tenancy/forms/bulk_edit.py:82 tenancy/forms/bulk_edit.py:130 +#: users/forms/bulk_edit.py:64 users/forms/bulk_edit.py:82 +#: users/forms/bulk_edit.py:112 virtualization/forms/bulk_edit.py:33 +#: virtualization/forms/bulk_edit.py:47 virtualization/forms/bulk_edit.py:82 +#: virtualization/forms/bulk_edit.py:159 virtualization/forms/bulk_edit.py:210 +#: virtualization/forms/bulk_edit.py:327 vpn/forms/bulk_edit.py:28 +#: vpn/forms/bulk_edit.py:64 vpn/forms/bulk_edit.py:121 +#: vpn/forms/bulk_edit.py:155 vpn/forms/bulk_edit.py:190 +#: vpn/forms/bulk_edit.py:215 vpn/forms/bulk_edit.py:247 +#: vpn/forms/bulk_edit.py:278 wireless/forms/bulk_edit.py:31 +#: wireless/forms/bulk_edit.py:85 wireless/forms/bulk_edit.py:144 msgid "Description" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:63 netbox/circuits/forms/bulk_edit.py:85 -#: netbox/circuits/forms/bulk_edit.py:135 -#: netbox/circuits/forms/bulk_import.py:43 -#: netbox/circuits/forms/bulk_import.py:58 -#: netbox/circuits/forms/bulk_import.py:81 -#: netbox/circuits/forms/filtersets.py:79 -#: netbox/circuits/forms/filtersets.py:97 -#: netbox/circuits/forms/filtersets.py:125 -#: netbox/circuits/forms/filtersets.py:143 -#: netbox/circuits/forms/filtersets.py:225 -#: netbox/circuits/forms/filtersets.py:269 -#: netbox/circuits/forms/filtersets.py:292 -#: netbox/circuits/forms/filtersets.py:330 -#: netbox/circuits/forms/filtersets.py:338 -#: netbox/circuits/forms/filtersets.py:374 -#: netbox/circuits/forms/filtersets.py:397 -#: netbox/circuits/forms/model_forms.py:60 -#: netbox/circuits/forms/model_forms.py:76 -#: netbox/circuits/forms/model_forms.py:110 -#: netbox/circuits/tables/circuits.py:57 netbox/circuits/tables/circuits.py:111 -#: netbox/circuits/tables/circuits.py:195 -#: netbox/circuits/tables/providers.py:70 -#: netbox/circuits/tables/providers.py:101 -#: netbox/circuits/tables/virtual_circuits.py:46 -#: netbox/circuits/tables/virtual_circuits.py:93 -#: netbox/templates/circuits/circuit.html:18 -#: netbox/templates/circuits/circuitgroupassignment.html:26 -#: netbox/templates/circuits/circuittermination.html:25 -#: netbox/templates/circuits/provider.html:20 -#: netbox/templates/circuits/provideraccount.html:20 -#: netbox/templates/circuits/providernetwork.html:20 -#: netbox/templates/circuits/virtualcircuit.html:23 -#: netbox/templates/circuits/virtualcircuittermination.html:26 -#: netbox/templates/dcim/inc/cable_termination.html:62 -#: netbox/templates/dcim/interface.html:166 +#: circuits/forms/bulk_edit.py:63 circuits/forms/bulk_edit.py:85 +#: circuits/forms/bulk_edit.py:135 circuits/forms/bulk_import.py:43 +#: circuits/forms/bulk_import.py:58 circuits/forms/bulk_import.py:81 +#: circuits/forms/filtersets.py:79 circuits/forms/filtersets.py:97 +#: circuits/forms/filtersets.py:125 circuits/forms/filtersets.py:143 +#: circuits/forms/filtersets.py:225 circuits/forms/filtersets.py:269 +#: circuits/forms/filtersets.py:292 circuits/forms/filtersets.py:330 +#: circuits/forms/filtersets.py:338 circuits/forms/filtersets.py:374 +#: circuits/forms/filtersets.py:397 circuits/forms/model_forms.py:60 +#: circuits/forms/model_forms.py:76 circuits/forms/model_forms.py:110 +#: circuits/tables/circuits.py:57 circuits/tables/circuits.py:111 +#: circuits/tables/circuits.py:195 circuits/tables/providers.py:70 +#: circuits/tables/providers.py:101 circuits/tables/virtual_circuits.py:46 +#: circuits/tables/virtual_circuits.py:93 templates/circuits/circuit.html:18 +#: templates/circuits/circuitgroupassignment.html:26 +#: templates/circuits/circuittermination.html:25 +#: templates/circuits/provider.html:20 +#: templates/circuits/provideraccount.html:20 +#: templates/circuits/providernetwork.html:20 +#: templates/circuits/virtualcircuit.html:23 +#: templates/circuits/virtualcircuittermination.html:26 +#: templates/dcim/inc/cable_termination.html:62 +#: templates/dcim/interface.html:166 msgid "Provider" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:92 -#: netbox/circuits/forms/filtersets.py:100 -#: netbox/templates/circuits/providernetwork.html:28 +#: circuits/forms/bulk_edit.py:92 circuits/forms/filtersets.py:100 +#: templates/circuits/providernetwork.html:28 msgid "Service ID" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:112 -#: netbox/circuits/forms/bulk_edit.py:303 -#: netbox/circuits/forms/filtersets.py:116 -#: netbox/circuits/forms/filtersets.py:321 netbox/dcim/forms/bulk_edit.py:210 -#: netbox/dcim/forms/bulk_edit.py:613 netbox/dcim/forms/bulk_edit.py:822 -#: netbox/dcim/forms/bulk_edit.py:1191 netbox/dcim/forms/bulk_edit.py:1218 -#: netbox/dcim/forms/bulk_edit.py:1742 netbox/dcim/forms/filtersets.py:1113 -#: netbox/dcim/forms/filtersets.py:1371 netbox/dcim/forms/filtersets.py:1519 -#: netbox/dcim/forms/filtersets.py:1543 netbox/dcim/tables/devices.py:737 -#: netbox/dcim/tables/devices.py:793 netbox/dcim/tables/devices.py:1034 -#: netbox/dcim/tables/devicetypes.py:256 netbox/dcim/tables/devicetypes.py:271 -#: netbox/dcim/tables/racks.py:33 netbox/extras/forms/bulk_edit.py:270 -#: netbox/extras/tables/tables.py:446 -#: netbox/templates/circuits/circuittype.html:30 -#: netbox/templates/circuits/virtualcircuittype.html:30 -#: netbox/templates/dcim/cable.html:40 netbox/templates/dcim/devicerole.html:34 -#: netbox/templates/dcim/frontport.html:40 -#: netbox/templates/dcim/inventoryitemrole.html:26 -#: netbox/templates/dcim/poweroutlet.html:44 -#: netbox/templates/dcim/rackrole.html:30 -#: netbox/templates/dcim/rearport.html:40 netbox/templates/extras/tag.html:26 +#: circuits/forms/bulk_edit.py:112 circuits/forms/bulk_edit.py:303 +#: circuits/forms/filtersets.py:116 circuits/forms/filtersets.py:321 +#: dcim/forms/bulk_edit.py:216 dcim/forms/bulk_edit.py:656 +#: dcim/forms/bulk_edit.py:866 dcim/forms/bulk_edit.py:1235 +#: dcim/forms/bulk_edit.py:1262 dcim/forms/bulk_edit.py:1789 +#: dcim/forms/filtersets.py:1132 dcim/forms/filtersets.py:1390 +#: dcim/forms/filtersets.py:1543 dcim/forms/filtersets.py:1567 +#: dcim/tables/devices.py:744 dcim/tables/devices.py:800 +#: dcim/tables/devices.py:1041 dcim/tables/devicetypes.py:256 +#: dcim/tables/devicetypes.py:271 dcim/tables/racks.py:33 +#: extras/forms/bulk_edit.py:303 extras/tables/tables.py:487 +#: templates/circuits/circuittype.html:30 +#: templates/circuits/virtualcircuittype.html:30 templates/dcim/cable.html:40 +#: templates/dcim/devicerole.html:38 templates/dcim/frontport.html:40 +#: templates/dcim/inventoryitemrole.html:26 templates/dcim/poweroutlet.html:48 +#: templates/dcim/rackrole.html:30 templates/dcim/rearport.html:40 +#: templates/extras/tag.html:26 msgid "Color" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:130 -#: netbox/circuits/forms/bulk_edit.py:331 -#: netbox/circuits/forms/bulk_import.py:94 -#: netbox/circuits/forms/bulk_import.py:221 -#: netbox/circuits/forms/filtersets.py:138 -#: netbox/circuits/forms/filtersets.py:359 -#: netbox/circuits/tables/circuits.py:65 netbox/circuits/tables/circuits.py:199 -#: netbox/circuits/tables/virtual_circuits.py:58 -#: netbox/core/forms/bulk_edit.py:18 netbox/core/forms/filtersets.py:33 -#: netbox/core/tables/change_logging.py:32 netbox/core/tables/data.py:20 -#: netbox/core/tables/jobs.py:18 netbox/dcim/forms/bulk_edit.py:800 -#: netbox/dcim/forms/bulk_edit.py:939 netbox/dcim/forms/bulk_edit.py:1007 -#: netbox/dcim/forms/bulk_edit.py:1026 netbox/dcim/forms/bulk_edit.py:1049 -#: netbox/dcim/forms/bulk_edit.py:1091 netbox/dcim/forms/bulk_edit.py:1135 -#: netbox/dcim/forms/bulk_edit.py:1186 netbox/dcim/forms/bulk_edit.py:1213 -#: netbox/dcim/forms/bulk_import.py:190 netbox/dcim/forms/bulk_import.py:269 -#: netbox/dcim/forms/bulk_import.py:735 netbox/dcim/forms/bulk_import.py:761 -#: netbox/dcim/forms/bulk_import.py:787 netbox/dcim/forms/bulk_import.py:807 -#: netbox/dcim/forms/bulk_import.py:893 netbox/dcim/forms/bulk_import.py:987 -#: netbox/dcim/forms/bulk_import.py:1029 netbox/dcim/forms/bulk_import.py:1350 -#: netbox/dcim/forms/bulk_import.py:1513 netbox/dcim/forms/filtersets.py:1004 -#: netbox/dcim/forms/filtersets.py:1103 netbox/dcim/forms/filtersets.py:1224 -#: netbox/dcim/forms/filtersets.py:1296 netbox/dcim/forms/filtersets.py:1321 -#: netbox/dcim/forms/filtersets.py:1345 netbox/dcim/forms/filtersets.py:1365 -#: netbox/dcim/forms/filtersets.py:1407 netbox/dcim/forms/filtersets.py:1514 -#: netbox/dcim/forms/filtersets.py:1538 netbox/dcim/forms/model_forms.py:714 -#: netbox/dcim/forms/model_forms.py:720 netbox/dcim/forms/object_import.py:84 -#: netbox/dcim/forms/object_import.py:113 -#: netbox/dcim/forms/object_import.py:146 netbox/dcim/tables/devices.py:188 -#: netbox/dcim/tables/devices.py:845 netbox/dcim/tables/power.py:77 -#: netbox/dcim/tables/racks.py:137 netbox/extras/forms/bulk_import.py:42 -#: netbox/extras/tables/tables.py:408 netbox/extras/tables/tables.py:468 -#: netbox/netbox/tables/tables.py:243 netbox/templates/circuits/circuit.html:30 -#: netbox/templates/circuits/virtualcircuit.html:39 -#: netbox/templates/circuits/virtualcircuittermination.html:64 -#: netbox/templates/core/datasource.html:38 netbox/templates/dcim/cable.html:15 -#: netbox/templates/dcim/consoleport.html:36 -#: netbox/templates/dcim/consoleserverport.html:36 -#: netbox/templates/dcim/frontport.html:36 -#: netbox/templates/dcim/interface.html:46 -#: netbox/templates/dcim/interface.html:226 -#: netbox/templates/dcim/interface.html:368 -#: netbox/templates/dcim/powerfeed.html:32 -#: netbox/templates/dcim/poweroutlet.html:36 -#: netbox/templates/dcim/powerport.html:36 -#: netbox/templates/dcim/rearport.html:36 -#: netbox/templates/extras/eventrule.html:74 -#: netbox/templates/virtualization/cluster.html:17 -#: netbox/templates/vpn/l2vpn.html:22 -#: netbox/templates/wireless/inc/authentication_attrs.html:8 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:14 -#: netbox/virtualization/forms/bulk_edit.py:61 -#: netbox/virtualization/forms/bulk_import.py:42 -#: netbox/virtualization/forms/filtersets.py:55 -#: netbox/virtualization/forms/model_forms.py:65 -#: netbox/virtualization/tables/clusters.py:66 -#: netbox/vpn/forms/bulk_edit.py:264 netbox/vpn/forms/bulk_import.py:264 -#: netbox/vpn/forms/filtersets.py:223 netbox/vpn/forms/model_forms.py:85 -#: netbox/vpn/forms/model_forms.py:120 netbox/vpn/forms/model_forms.py:232 +#: circuits/forms/bulk_edit.py:130 circuits/forms/bulk_edit.py:331 +#: circuits/forms/bulk_import.py:94 circuits/forms/bulk_import.py:221 +#: circuits/forms/filtersets.py:138 circuits/forms/filtersets.py:359 +#: circuits/tables/circuits.py:65 circuits/tables/circuits.py:199 +#: circuits/tables/virtual_circuits.py:58 core/forms/bulk_edit.py:19 +#: core/forms/filtersets.py:33 core/tables/change_logging.py:32 +#: core/tables/data.py:20 core/tables/jobs.py:18 dcim/forms/bulk_edit.py:844 +#: dcim/forms/bulk_edit.py:983 dcim/forms/bulk_edit.py:1051 +#: dcim/forms/bulk_edit.py:1070 dcim/forms/bulk_edit.py:1093 +#: dcim/forms/bulk_edit.py:1135 dcim/forms/bulk_edit.py:1179 +#: dcim/forms/bulk_edit.py:1230 dcim/forms/bulk_edit.py:1257 +#: dcim/forms/bulk_import.py:194 dcim/forms/bulk_import.py:273 +#: dcim/forms/bulk_import.py:766 dcim/forms/bulk_import.py:792 +#: dcim/forms/bulk_import.py:818 dcim/forms/bulk_import.py:838 +#: dcim/forms/bulk_import.py:924 dcim/forms/bulk_import.py:1018 +#: dcim/forms/bulk_import.py:1060 dcim/forms/bulk_import.py:1381 +#: dcim/forms/bulk_import.py:1544 dcim/forms/filtersets.py:1023 +#: dcim/forms/filtersets.py:1122 dcim/forms/filtersets.py:1243 +#: dcim/forms/filtersets.py:1315 dcim/forms/filtersets.py:1340 +#: dcim/forms/filtersets.py:1364 dcim/forms/filtersets.py:1384 +#: dcim/forms/filtersets.py:1431 dcim/forms/filtersets.py:1538 +#: dcim/forms/filtersets.py:1562 dcim/forms/model_forms.py:808 +#: dcim/forms/model_forms.py:814 dcim/forms/object_import.py:84 +#: dcim/forms/object_import.py:113 dcim/forms/object_import.py:146 +#: dcim/tables/devices.py:188 dcim/tables/devices.py:852 +#: dcim/tables/power.py:77 dcim/tables/racks.py:141 +#: extras/forms/bulk_import.py:42 extras/tables/tables.py:449 +#: extras/tables/tables.py:509 netbox/tables/tables.py:269 +#: templates/circuits/circuit.html:30 templates/circuits/virtualcircuit.html:39 +#: templates/circuits/virtualcircuittermination.html:64 +#: templates/core/datasource.html:38 templates/dcim/cable.html:15 +#: templates/dcim/consoleport.html:36 templates/dcim/consoleserverport.html:36 +#: templates/dcim/frontport.html:36 templates/dcim/interface.html:46 +#: templates/dcim/interface.html:226 templates/dcim/interface.html:368 +#: templates/dcim/powerfeed.html:32 templates/dcim/poweroutlet.html:36 +#: templates/dcim/powerport.html:36 templates/dcim/rearport.html:36 +#: templates/extras/eventrule.html:74 templates/virtualization/cluster.html:17 +#: templates/vpn/l2vpn.html:22 +#: templates/wireless/inc/authentication_attrs.html:8 +#: templates/wireless/inc/wirelesslink_interface.html:14 +#: virtualization/forms/bulk_edit.py:61 virtualization/forms/bulk_import.py:42 +#: virtualization/forms/filtersets.py:55 virtualization/forms/model_forms.py:65 +#: virtualization/tables/clusters.py:66 vpn/forms/bulk_edit.py:268 +#: vpn/forms/bulk_import.py:269 vpn/forms/filtersets.py:228 +#: vpn/forms/model_forms.py:85 vpn/forms/model_forms.py:120 +#: vpn/forms/model_forms.py:232 msgid "Type" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:140 -#: netbox/circuits/forms/bulk_edit.py:326 -#: netbox/circuits/forms/bulk_import.py:87 -#: netbox/circuits/forms/bulk_import.py:214 -#: netbox/circuits/forms/filtersets.py:151 -#: netbox/circuits/forms/filtersets.py:346 -#: netbox/circuits/forms/model_forms.py:116 -#: netbox/circuits/forms/model_forms.py:330 -#: netbox/templates/circuits/virtualcircuit.html:31 -#: netbox/templates/circuits/virtualcircuittermination.html:34 +#: circuits/forms/bulk_edit.py:140 circuits/forms/bulk_edit.py:326 +#: circuits/forms/bulk_import.py:87 circuits/forms/bulk_import.py:214 +#: circuits/forms/filtersets.py:151 circuits/forms/filtersets.py:346 +#: circuits/forms/model_forms.py:116 circuits/forms/model_forms.py:330 +#: templates/circuits/virtualcircuit.html:31 +#: templates/circuits/virtualcircuittermination.html:34 msgid "Provider account" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:148 -#: netbox/circuits/forms/bulk_edit.py:336 -#: netbox/circuits/forms/bulk_import.py:100 -#: netbox/circuits/forms/bulk_import.py:227 -#: netbox/circuits/forms/filtersets.py:162 -#: netbox/circuits/forms/filtersets.py:362 netbox/core/forms/filtersets.py:38 -#: netbox/core/forms/filtersets.py:80 netbox/core/tables/data.py:23 -#: netbox/core/tables/jobs.py:26 netbox/core/tables/tasks.py:88 -#: netbox/dcim/forms/bulk_edit.py:110 netbox/dcim/forms/bulk_edit.py:185 -#: netbox/dcim/forms/bulk_edit.py:355 netbox/dcim/forms/bulk_edit.py:709 -#: netbox/dcim/forms/bulk_edit.py:774 netbox/dcim/forms/bulk_edit.py:806 -#: netbox/dcim/forms/bulk_edit.py:933 netbox/dcim/forms/bulk_edit.py:1723 -#: netbox/dcim/forms/bulk_edit.py:1765 netbox/dcim/forms/bulk_import.py:90 -#: netbox/dcim/forms/bulk_import.py:149 netbox/dcim/forms/bulk_import.py:250 -#: netbox/dcim/forms/bulk_import.py:532 netbox/dcim/forms/bulk_import.py:686 -#: netbox/dcim/forms/bulk_import.py:1137 netbox/dcim/forms/bulk_import.py:1344 -#: netbox/dcim/forms/bulk_import.py:1508 netbox/dcim/forms/bulk_import.py:1572 -#: netbox/dcim/forms/filtersets.py:179 netbox/dcim/forms/filtersets.py:238 -#: netbox/dcim/forms/filtersets.py:360 netbox/dcim/forms/filtersets.py:800 -#: netbox/dcim/forms/filtersets.py:925 netbox/dcim/forms/filtersets.py:1007 -#: netbox/dcim/forms/filtersets.py:1108 netbox/dcim/forms/filtersets.py:1219 -#: netbox/dcim/forms/filtersets.py:1621 netbox/dcim/tables/devices.py:150 -#: netbox/dcim/tables/devices.py:848 netbox/dcim/tables/devices.py:982 -#: netbox/dcim/tables/devices.py:1094 netbox/dcim/tables/modules.py:70 -#: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:125 -#: netbox/dcim/tables/sites.py:82 netbox/dcim/tables/sites.py:137 -#: netbox/ipam/forms/bulk_edit.py:240 netbox/ipam/forms/bulk_edit.py:290 -#: netbox/ipam/forms/bulk_edit.py:338 netbox/ipam/forms/bulk_edit.py:490 -#: netbox/ipam/forms/bulk_import.py:195 netbox/ipam/forms/bulk_import.py:263 -#: netbox/ipam/forms/bulk_import.py:299 netbox/ipam/forms/bulk_import.py:489 -#: netbox/ipam/forms/filtersets.py:219 netbox/ipam/forms/filtersets.py:297 -#: netbox/ipam/forms/filtersets.py:372 netbox/ipam/forms/filtersets.py:556 -#: netbox/ipam/forms/model_forms.py:511 netbox/ipam/tables/ip.py:183 -#: netbox/ipam/tables/ip.py:264 netbox/ipam/tables/ip.py:315 -#: netbox/ipam/tables/ip.py:378 netbox/ipam/tables/ip.py:405 -#: netbox/ipam/tables/vlans.py:95 netbox/ipam/tables/vlans.py:208 -#: netbox/templates/circuits/circuit.html:34 -#: netbox/templates/circuits/virtualcircuit.html:43 -#: netbox/templates/core/datasource.html:46 netbox/templates/core/job.html:48 -#: netbox/templates/core/rq_task.html:81 netbox/templates/core/system.html:18 -#: netbox/templates/dcim/cable.html:19 netbox/templates/dcim/device.html:178 -#: netbox/templates/dcim/inventoryitem.html:36 -#: netbox/templates/dcim/location.html:45 netbox/templates/dcim/module.html:69 -#: netbox/templates/dcim/powerfeed.html:36 netbox/templates/dcim/rack.html:41 -#: netbox/templates/dcim/site.html:43 -#: netbox/templates/extras/script_list.html:48 -#: netbox/templates/ipam/ipaddress.html:37 -#: netbox/templates/ipam/iprange.html:54 netbox/templates/ipam/prefix.html:69 -#: netbox/templates/ipam/vlan.html:48 -#: netbox/templates/virtualization/cluster.html:21 -#: netbox/templates/virtualization/virtualmachine.html:19 -#: netbox/templates/vpn/tunnel.html:25 -#: netbox/templates/wireless/wirelesslan.html:22 -#: netbox/templates/wireless/wirelesslink.html:17 -#: netbox/users/forms/filtersets.py:32 netbox/users/forms/model_forms.py:194 -#: netbox/virtualization/forms/bulk_edit.py:71 -#: netbox/virtualization/forms/bulk_edit.py:100 -#: netbox/virtualization/forms/bulk_import.py:55 -#: netbox/virtualization/forms/bulk_import.py:86 -#: netbox/virtualization/forms/filtersets.py:83 -#: netbox/virtualization/forms/filtersets.py:166 -#: netbox/virtualization/tables/clusters.py:74 -#: netbox/virtualization/tables/virtualmachines.py:30 -#: netbox/vpn/forms/bulk_edit.py:39 netbox/vpn/forms/bulk_import.py:37 -#: netbox/vpn/forms/filtersets.py:52 netbox/vpn/tables/tunnels.py:48 -#: netbox/wireless/forms/bulk_edit.py:45 netbox/wireless/forms/bulk_edit.py:108 -#: netbox/wireless/forms/bulk_import.py:45 -#: netbox/wireless/forms/bulk_import.py:89 -#: netbox/wireless/forms/filtersets.py:52 -#: netbox/wireless/forms/filtersets.py:111 -#: netbox/wireless/tables/wirelesslan.py:52 -#: netbox/wireless/tables/wirelesslink.py:19 +#: circuits/forms/bulk_edit.py:148 circuits/forms/bulk_edit.py:336 +#: circuits/forms/bulk_import.py:100 circuits/forms/bulk_import.py:227 +#: circuits/forms/filtersets.py:162 circuits/forms/filtersets.py:362 +#: core/forms/filtersets.py:38 core/forms/filtersets.py:85 +#: core/tables/data.py:23 core/tables/jobs.py:26 core/tables/tasks.py:88 +#: dcim/forms/bulk_edit.py:115 dcim/forms/bulk_edit.py:190 +#: dcim/forms/bulk_edit.py:366 dcim/forms/bulk_edit.py:753 +#: dcim/forms/bulk_edit.py:818 dcim/forms/bulk_edit.py:850 +#: dcim/forms/bulk_edit.py:977 dcim/forms/bulk_edit.py:1770 +#: dcim/forms/bulk_edit.py:1812 dcim/forms/bulk_import.py:91 +#: dcim/forms/bulk_import.py:150 dcim/forms/bulk_import.py:254 +#: dcim/forms/bulk_import.py:563 dcim/forms/bulk_import.py:717 +#: dcim/forms/bulk_import.py:1168 dcim/forms/bulk_import.py:1375 +#: dcim/forms/bulk_import.py:1539 dcim/forms/bulk_import.py:1603 +#: dcim/forms/filtersets.py:180 dcim/forms/filtersets.py:239 +#: dcim/forms/filtersets.py:361 dcim/forms/filtersets.py:819 +#: dcim/forms/filtersets.py:944 dcim/forms/filtersets.py:1026 +#: dcim/forms/filtersets.py:1127 dcim/forms/filtersets.py:1238 +#: dcim/forms/filtersets.py:1394 dcim/forms/filtersets.py:1645 +#: dcim/tables/devices.py:150 dcim/tables/devices.py:524 +#: dcim/tables/devices.py:855 dcim/tables/devices.py:989 +#: dcim/tables/devices.py:1101 dcim/tables/modules.py:104 +#: dcim/tables/power.py:74 dcim/tables/racks.py:129 dcim/tables/sites.py:88 +#: dcim/tables/sites.py:143 ipam/forms/bulk_edit.py:240 +#: ipam/forms/bulk_edit.py:290 ipam/forms/bulk_edit.py:343 +#: ipam/forms/bulk_edit.py:501 ipam/forms/bulk_import.py:195 +#: ipam/forms/bulk_import.py:263 ipam/forms/bulk_import.py:299 +#: ipam/forms/bulk_import.py:496 ipam/forms/filtersets.py:219 +#: ipam/forms/filtersets.py:297 ipam/forms/filtersets.py:379 +#: ipam/forms/filtersets.py:564 ipam/forms/model_forms.py:511 +#: ipam/tables/ip.py:184 ipam/tables/ip.py:265 ipam/tables/ip.py:321 +#: ipam/tables/ip.py:394 ipam/tables/ip.py:421 ipam/tables/vlans.py:97 +#: ipam/tables/vlans.py:210 templates/circuits/circuit.html:34 +#: templates/circuits/virtualcircuit.html:43 templates/core/datasource.html:46 +#: templates/core/job.html:48 templates/core/rq_task.html:81 +#: templates/core/system.html:18 templates/dcim/cable.html:19 +#: templates/dcim/device.html:178 templates/dcim/inventoryitem.html:36 +#: templates/dcim/location.html:45 templates/dcim/module.html:69 +#: templates/dcim/powerfeed.html:36 templates/dcim/poweroutlet.html:40 +#: templates/dcim/rack.html:41 templates/dcim/site.html:43 +#: templates/extras/script_list.html:48 templates/ipam/ipaddress.html:37 +#: templates/ipam/iprange.html:61 templates/ipam/prefix.html:69 +#: templates/ipam/vlan.html:48 templates/virtualization/cluster.html:21 +#: templates/virtualization/virtualmachine.html:19 templates/vpn/l2vpn.html:26 +#: templates/vpn/tunnel.html:25 templates/wireless/wirelesslan.html:22 +#: templates/wireless/wirelesslink.html:17 users/forms/filtersets.py:32 +#: users/forms/model_forms.py:194 virtualization/forms/bulk_edit.py:71 +#: virtualization/forms/bulk_edit.py:100 virtualization/forms/bulk_import.py:55 +#: virtualization/forms/bulk_import.py:86 virtualization/forms/filtersets.py:83 +#: virtualization/forms/filtersets.py:166 virtualization/tables/clusters.py:74 +#: virtualization/tables/virtualmachines.py:30 vpn/forms/bulk_edit.py:39 +#: vpn/forms/bulk_edit.py:264 vpn/forms/bulk_import.py:37 +#: vpn/forms/bulk_import.py:264 vpn/forms/filtersets.py:52 +#: vpn/forms/filtersets.py:223 vpn/tables/l2vpn.py:27 vpn/tables/tunnels.py:48 +#: wireless/forms/bulk_edit.py:46 wireless/forms/bulk_edit.py:109 +#: wireless/forms/bulk_import.py:45 wireless/forms/bulk_import.py:89 +#: wireless/forms/filtersets.py:52 wireless/forms/filtersets.py:111 +#: wireless/tables/wirelesslan.py:52 wireless/tables/wirelesslink.py:19 msgid "Status" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:154 -#: netbox/circuits/forms/bulk_edit.py:271 -#: netbox/circuits/forms/bulk_edit.py:342 -#: netbox/circuits/forms/bulk_import.py:111 -#: netbox/circuits/forms/bulk_import.py:170 -#: netbox/circuits/forms/bulk_import.py:232 -#: netbox/circuits/forms/filtersets.py:131 -#: netbox/circuits/forms/filtersets.py:278 -#: netbox/circuits/forms/filtersets.py:332 netbox/dcim/forms/bulk_edit.py:126 -#: netbox/dcim/forms/bulk_edit.py:191 netbox/dcim/forms/bulk_edit.py:350 -#: netbox/dcim/forms/bulk_edit.py:470 netbox/dcim/forms/bulk_edit.py:699 -#: netbox/dcim/forms/bulk_edit.py:812 netbox/dcim/forms/bulk_edit.py:1770 -#: netbox/dcim/forms/bulk_import.py:109 netbox/dcim/forms/bulk_import.py:154 -#: netbox/dcim/forms/bulk_import.py:243 netbox/dcim/forms/bulk_import.py:358 -#: netbox/dcim/forms/bulk_import.py:506 netbox/dcim/forms/bulk_import.py:1356 -#: netbox/dcim/forms/bulk_import.py:1565 netbox/dcim/forms/filtersets.py:174 -#: netbox/dcim/forms/filtersets.py:206 netbox/dcim/forms/filtersets.py:324 -#: netbox/dcim/forms/filtersets.py:400 netbox/dcim/forms/filtersets.py:421 -#: netbox/dcim/forms/filtersets.py:723 netbox/dcim/forms/filtersets.py:917 -#: netbox/dcim/forms/filtersets.py:1027 netbox/dcim/forms/filtersets.py:1057 -#: netbox/dcim/forms/filtersets.py:1179 netbox/dcim/tables/power.py:88 -#: netbox/extras/filtersets.py:612 netbox/extras/forms/filtersets.py:330 -#: netbox/extras/forms/filtersets.py:403 netbox/ipam/forms/bulk_edit.py:46 -#: netbox/ipam/forms/bulk_edit.py:71 netbox/ipam/forms/bulk_edit.py:115 -#: netbox/ipam/forms/bulk_edit.py:144 netbox/ipam/forms/bulk_edit.py:169 -#: netbox/ipam/forms/bulk_edit.py:235 netbox/ipam/forms/bulk_edit.py:285 -#: netbox/ipam/forms/bulk_edit.py:333 netbox/ipam/forms/bulk_edit.py:485 -#: netbox/ipam/forms/bulk_import.py:41 netbox/ipam/forms/bulk_import.py:70 -#: netbox/ipam/forms/bulk_import.py:98 netbox/ipam/forms/bulk_import.py:118 -#: netbox/ipam/forms/bulk_import.py:138 netbox/ipam/forms/bulk_import.py:167 -#: netbox/ipam/forms/bulk_import.py:256 netbox/ipam/forms/bulk_import.py:292 -#: netbox/ipam/forms/bulk_import.py:482 netbox/ipam/forms/filtersets.py:50 -#: netbox/ipam/forms/filtersets.py:70 netbox/ipam/forms/filtersets.py:102 -#: netbox/ipam/forms/filtersets.py:123 netbox/ipam/forms/filtersets.py:146 -#: netbox/ipam/forms/filtersets.py:182 netbox/ipam/forms/filtersets.py:282 -#: netbox/ipam/forms/filtersets.py:326 netbox/ipam/forms/filtersets.py:524 -#: netbox/ipam/tables/ip.py:408 netbox/ipam/tables/vlans.py:205 -#: netbox/templates/circuits/circuit.html:48 -#: netbox/templates/circuits/circuitgroup.html:36 -#: netbox/templates/circuits/virtualcircuit.html:47 -#: netbox/templates/dcim/cable.html:23 netbox/templates/dcim/device.html:79 -#: netbox/templates/dcim/location.html:49 -#: netbox/templates/dcim/powerfeed.html:44 netbox/templates/dcim/rack.html:32 -#: netbox/templates/dcim/rackreservation.html:49 -#: netbox/templates/dcim/site.html:47 -#: netbox/templates/dcim/virtualdevicecontext.html:52 -#: netbox/templates/ipam/aggregate.html:30 netbox/templates/ipam/asn.html:33 -#: netbox/templates/ipam/asnrange.html:29 -#: netbox/templates/ipam/ipaddress.html:28 -#: netbox/templates/ipam/iprange.html:58 netbox/templates/ipam/prefix.html:29 -#: netbox/templates/ipam/routetarget.html:17 netbox/templates/ipam/vlan.html:39 -#: netbox/templates/ipam/vrf.html:20 netbox/templates/tenancy/tenant.html:17 -#: netbox/templates/virtualization/cluster.html:33 -#: netbox/templates/virtualization/virtualmachine.html:39 -#: netbox/templates/vpn/l2vpn.html:30 netbox/templates/vpn/tunnel.html:49 -#: netbox/templates/wireless/wirelesslan.html:42 -#: netbox/templates/wireless/wirelesslink.html:25 -#: netbox/tenancy/forms/forms.py:25 netbox/tenancy/forms/forms.py:49 -#: netbox/tenancy/forms/model_forms.py:52 netbox/tenancy/tables/columns.py:49 -#: netbox/virtualization/forms/bulk_edit.py:77 -#: netbox/virtualization/forms/bulk_edit.py:137 -#: netbox/virtualization/forms/bulk_import.py:67 -#: netbox/virtualization/forms/bulk_import.py:121 -#: netbox/virtualization/forms/filtersets.py:48 -#: netbox/virtualization/forms/filtersets.py:111 -#: netbox/vpn/forms/bulk_edit.py:59 netbox/vpn/forms/bulk_edit.py:269 -#: netbox/vpn/forms/bulk_import.py:59 netbox/vpn/forms/bulk_import.py:258 -#: netbox/vpn/forms/filtersets.py:219 netbox/wireless/forms/bulk_edit.py:65 -#: netbox/wireless/forms/bulk_edit.py:113 -#: netbox/wireless/forms/bulk_import.py:57 -#: netbox/wireless/forms/bulk_import.py:102 -#: netbox/wireless/forms/filtersets.py:38 -#: netbox/wireless/forms/filtersets.py:103 +#: circuits/forms/bulk_edit.py:154 circuits/forms/bulk_edit.py:271 +#: circuits/forms/bulk_edit.py:342 circuits/forms/bulk_import.py:111 +#: circuits/forms/bulk_import.py:170 circuits/forms/bulk_import.py:232 +#: circuits/forms/filtersets.py:131 circuits/forms/filtersets.py:278 +#: circuits/forms/filtersets.py:332 dcim/forms/bulk_edit.py:131 +#: dcim/forms/bulk_edit.py:196 dcim/forms/bulk_edit.py:361 +#: dcim/forms/bulk_edit.py:484 dcim/forms/bulk_edit.py:743 +#: dcim/forms/bulk_edit.py:856 dcim/forms/bulk_edit.py:1817 +#: dcim/forms/bulk_import.py:110 dcim/forms/bulk_import.py:155 +#: dcim/forms/bulk_import.py:247 dcim/forms/bulk_import.py:362 +#: dcim/forms/bulk_import.py:537 dcim/forms/bulk_import.py:1387 +#: dcim/forms/bulk_import.py:1596 dcim/forms/filtersets.py:175 +#: dcim/forms/filtersets.py:207 dcim/forms/filtersets.py:325 +#: dcim/forms/filtersets.py:401 dcim/forms/filtersets.py:422 +#: dcim/forms/filtersets.py:742 dcim/forms/filtersets.py:936 +#: dcim/forms/filtersets.py:1046 dcim/forms/filtersets.py:1076 +#: dcim/forms/filtersets.py:1198 dcim/tables/power.py:88 +#: extras/filtersets.py:705 extras/forms/filtersets.py:365 +#: extras/forms/filtersets.py:438 ipam/forms/bulk_edit.py:46 +#: ipam/forms/bulk_edit.py:71 ipam/forms/bulk_edit.py:115 +#: ipam/forms/bulk_edit.py:144 ipam/forms/bulk_edit.py:169 +#: ipam/forms/bulk_edit.py:235 ipam/forms/bulk_edit.py:285 +#: ipam/forms/bulk_edit.py:338 ipam/forms/bulk_edit.py:439 +#: ipam/forms/bulk_edit.py:496 ipam/forms/bulk_import.py:41 +#: ipam/forms/bulk_import.py:70 ipam/forms/bulk_import.py:98 +#: ipam/forms/bulk_import.py:118 ipam/forms/bulk_import.py:138 +#: ipam/forms/bulk_import.py:167 ipam/forms/bulk_import.py:256 +#: ipam/forms/bulk_import.py:292 ipam/forms/bulk_import.py:458 +#: ipam/forms/bulk_import.py:489 ipam/forms/filtersets.py:50 +#: ipam/forms/filtersets.py:70 ipam/forms/filtersets.py:102 +#: ipam/forms/filtersets.py:123 ipam/forms/filtersets.py:146 +#: ipam/forms/filtersets.py:182 ipam/forms/filtersets.py:282 +#: ipam/forms/filtersets.py:333 ipam/forms/filtersets.py:441 +#: ipam/forms/filtersets.py:532 ipam/tables/ip.py:424 ipam/tables/vlans.py:207 +#: templates/circuits/circuit.html:48 templates/circuits/circuitgroup.html:36 +#: templates/circuits/virtualcircuit.html:47 templates/dcim/cable.html:23 +#: templates/dcim/device.html:79 templates/dcim/location.html:49 +#: templates/dcim/powerfeed.html:44 templates/dcim/rack.html:32 +#: templates/dcim/rackreservation.html:49 templates/dcim/site.html:47 +#: templates/dcim/virtualdevicecontext.html:52 templates/ipam/aggregate.html:30 +#: templates/ipam/asn.html:33 templates/ipam/asnrange.html:29 +#: templates/ipam/ipaddress.html:28 templates/ipam/iprange.html:65 +#: templates/ipam/prefix.html:29 templates/ipam/routetarget.html:17 +#: templates/ipam/vlan.html:39 templates/ipam/vlangroup.html:50 +#: templates/ipam/vrf.html:20 templates/tenancy/tenant.html:17 +#: templates/virtualization/cluster.html:33 +#: templates/virtualization/virtualmachine.html:39 templates/vpn/l2vpn.html:34 +#: templates/vpn/tunnel.html:49 templates/wireless/wirelesslan.html:42 +#: templates/wireless/wirelesslink.html:25 tenancy/forms/forms.py:25 +#: tenancy/forms/forms.py:49 tenancy/forms/model_forms.py:53 +#: tenancy/tables/columns.py:49 virtualization/forms/bulk_edit.py:77 +#: virtualization/forms/bulk_edit.py:137 virtualization/forms/bulk_import.py:67 +#: virtualization/forms/bulk_import.py:121 +#: virtualization/forms/filtersets.py:48 virtualization/forms/filtersets.py:111 +#: vpn/forms/bulk_edit.py:59 vpn/forms/bulk_edit.py:273 +#: vpn/forms/bulk_import.py:59 vpn/forms/bulk_import.py:258 +#: vpn/forms/filtersets.py:219 wireless/forms/bulk_edit.py:66 +#: wireless/forms/bulk_edit.py:114 wireless/forms/bulk_import.py:57 +#: wireless/forms/bulk_import.py:102 wireless/forms/filtersets.py:38 +#: wireless/forms/filtersets.py:103 msgid "Tenant" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:159 -#: netbox/circuits/forms/filtersets.py:191 +#: circuits/forms/bulk_edit.py:159 circuits/forms/filtersets.py:191 msgid "Install date" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:164 -#: netbox/circuits/forms/filtersets.py:196 +#: circuits/forms/bulk_edit.py:164 circuits/forms/filtersets.py:196 msgid "Termination date" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:170 -#: netbox/circuits/forms/filtersets.py:203 +#: circuits/forms/bulk_edit.py:170 circuits/forms/filtersets.py:203 msgid "Commit rate (Kbps)" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:176 -#: netbox/circuits/forms/filtersets.py:209 -#: netbox/circuits/forms/model_forms.py:136 -#: netbox/templates/circuits/circuit.html:38 -#: netbox/templates/wireless/wirelesslink.html:38 -#: netbox/wireless/forms/bulk_edit.py:132 -#: netbox/wireless/forms/filtersets.py:130 -#: netbox/wireless/forms/model_forms.py:168 +#: circuits/forms/bulk_edit.py:176 circuits/forms/filtersets.py:209 +#: circuits/forms/model_forms.py:136 templates/circuits/circuit.html:38 +#: templates/wireless/wirelesslink.html:38 wireless/forms/bulk_edit.py:133 +#: wireless/forms/filtersets.py:130 wireless/forms/model_forms.py:169 msgid "Distance" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:181 -#: netbox/circuits/forms/bulk_import.py:105 -#: netbox/circuits/forms/bulk_import.py:108 -#: netbox/circuits/forms/filtersets.py:213 -#: netbox/wireless/forms/bulk_edit.py:137 -#: netbox/wireless/forms/bulk_import.py:121 -#: netbox/wireless/forms/bulk_import.py:124 -#: netbox/wireless/forms/filtersets.py:134 +#: circuits/forms/bulk_edit.py:181 circuits/forms/bulk_import.py:105 +#: circuits/forms/bulk_import.py:108 circuits/forms/filtersets.py:213 +#: wireless/forms/bulk_edit.py:138 wireless/forms/bulk_import.py:121 +#: wireless/forms/bulk_import.py:124 wireless/forms/filtersets.py:134 msgid "Distance unit" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:196 -#: netbox/circuits/forms/model_forms.py:141 +#: circuits/forms/bulk_edit.py:196 circuits/forms/model_forms.py:141 msgid "Service Parameters" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:197 -#: netbox/circuits/forms/filtersets.py:73 -#: netbox/circuits/forms/filtersets.py:92 -#: netbox/circuits/forms/filtersets.py:111 -#: netbox/circuits/forms/filtersets.py:128 -#: netbox/circuits/forms/filtersets.py:316 -#: netbox/circuits/forms/filtersets.py:331 netbox/core/forms/filtersets.py:68 -#: netbox/core/forms/filtersets.py:136 netbox/dcim/forms/bulk_edit.py:846 -#: netbox/dcim/forms/filtersets.py:173 netbox/dcim/forms/filtersets.py:205 -#: netbox/dcim/forms/filtersets.py:916 netbox/dcim/forms/filtersets.py:1056 -#: netbox/dcim/forms/filtersets.py:1180 netbox/dcim/forms/filtersets.py:1288 -#: netbox/dcim/forms/filtersets.py:1312 netbox/dcim/forms/filtersets.py:1337 -#: netbox/dcim/forms/filtersets.py:1356 netbox/dcim/forms/filtersets.py:1380 -#: netbox/dcim/forms/filtersets.py:1505 netbox/dcim/forms/filtersets.py:1529 -#: netbox/dcim/forms/filtersets.py:1553 netbox/dcim/forms/filtersets.py:1571 -#: netbox/dcim/forms/filtersets.py:1587 netbox/extras/forms/bulk_edit.py:90 -#: netbox/extras/forms/filtersets.py:45 netbox/extras/forms/filtersets.py:137 -#: netbox/extras/forms/filtersets.py:169 netbox/extras/forms/filtersets.py:210 -#: netbox/extras/forms/filtersets.py:227 netbox/extras/forms/filtersets.py:258 -#: netbox/extras/forms/filtersets.py:282 netbox/extras/forms/filtersets.py:449 -#: netbox/ipam/forms/filtersets.py:101 netbox/ipam/forms/filtersets.py:281 -#: netbox/ipam/forms/filtersets.py:323 netbox/ipam/forms/filtersets.py:399 -#: netbox/ipam/forms/filtersets.py:484 netbox/ipam/forms/filtersets.py:497 -#: netbox/ipam/forms/filtersets.py:522 netbox/ipam/forms/filtersets.py:593 -#: netbox/ipam/forms/filtersets.py:611 netbox/netbox/tables/tables.py:259 -#: netbox/virtualization/forms/filtersets.py:46 -#: netbox/virtualization/forms/filtersets.py:109 -#: netbox/virtualization/forms/filtersets.py:204 -#: netbox/virtualization/forms/filtersets.py:261 -#: netbox/vpn/forms/filtersets.py:218 netbox/wireless/forms/bulk_edit.py:153 -#: netbox/wireless/forms/filtersets.py:36 -#: netbox/wireless/forms/filtersets.py:102 +#: circuits/forms/bulk_edit.py:197 circuits/forms/filtersets.py:73 +#: circuits/forms/filtersets.py:92 circuits/forms/filtersets.py:111 +#: circuits/forms/filtersets.py:128 circuits/forms/filtersets.py:316 +#: circuits/forms/filtersets.py:331 core/forms/filtersets.py:73 +#: core/forms/filtersets.py:141 dcim/forms/bulk_edit.py:890 +#: dcim/forms/filtersets.py:174 dcim/forms/filtersets.py:206 +#: dcim/forms/filtersets.py:935 dcim/forms/filtersets.py:1075 +#: dcim/forms/filtersets.py:1199 dcim/forms/filtersets.py:1307 +#: dcim/forms/filtersets.py:1331 dcim/forms/filtersets.py:1356 +#: dcim/forms/filtersets.py:1375 dcim/forms/filtersets.py:1404 +#: dcim/forms/filtersets.py:1529 dcim/forms/filtersets.py:1553 +#: dcim/forms/filtersets.py:1577 dcim/forms/filtersets.py:1595 +#: dcim/forms/filtersets.py:1611 dcim/tables/modules.py:24 +#: extras/forms/bulk_edit.py:91 extras/forms/filtersets.py:46 +#: extras/forms/filtersets.py:138 extras/forms/filtersets.py:215 +#: extras/forms/filtersets.py:232 extras/forms/filtersets.py:262 +#: extras/forms/filtersets.py:293 extras/forms/filtersets.py:317 +#: extras/forms/filtersets.py:504 ipam/forms/filtersets.py:101 +#: ipam/forms/filtersets.py:281 ipam/forms/filtersets.py:330 +#: ipam/forms/filtersets.py:406 ipam/forms/filtersets.py:492 +#: ipam/forms/filtersets.py:505 ipam/forms/filtersets.py:530 +#: ipam/forms/filtersets.py:601 ipam/forms/filtersets.py:619 +#: netbox/tables/tables.py:285 templates/dcim/moduletype.html:68 +#: virtualization/forms/filtersets.py:46 virtualization/forms/filtersets.py:109 +#: virtualization/forms/filtersets.py:204 +#: virtualization/forms/filtersets.py:261 vpn/forms/filtersets.py:218 +#: wireless/forms/bulk_edit.py:154 wireless/forms/filtersets.py:36 +#: wireless/forms/filtersets.py:102 msgid "Attributes" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:198 -#: netbox/circuits/forms/bulk_edit.py:356 -#: netbox/circuits/forms/model_forms.py:142 -#: netbox/circuits/forms/model_forms.py:240 -#: netbox/circuits/forms/model_forms.py:345 -#: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 -#: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 -#: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 -#: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 -#: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 -#: netbox/ipam/forms/model_forms.py:271 netbox/ipam/forms/model_forms.py:330 -#: netbox/netbox/navigation/menu.py:24 -#: netbox/templates/dcim/device_edit.html:87 -#: netbox/templates/dcim/htmx/cable_edit.html:75 -#: netbox/templates/ipam/ipaddress_bulk_add.html:27 -#: netbox/templates/ipam/vlan_edit.html:34 -#: netbox/virtualization/forms/model_forms.py:80 -#: netbox/virtualization/forms/model_forms.py:229 -#: netbox/vpn/forms/bulk_edit.py:78 netbox/vpn/forms/filtersets.py:48 -#: netbox/vpn/forms/model_forms.py:63 netbox/vpn/forms/model_forms.py:148 -#: netbox/vpn/forms/model_forms.py:414 netbox/wireless/forms/model_forms.py:57 -#: netbox/wireless/forms/model_forms.py:173 +#: circuits/forms/bulk_edit.py:198 circuits/forms/bulk_edit.py:356 +#: circuits/forms/model_forms.py:142 circuits/forms/model_forms.py:240 +#: circuits/forms/model_forms.py:345 dcim/forms/model_forms.py:148 +#: dcim/forms/model_forms.py:191 dcim/forms/model_forms.py:281 +#: dcim/forms/model_forms.py:339 dcim/forms/model_forms.py:874 +#: dcim/forms/model_forms.py:1869 ipam/forms/bulk_edit.py:448 +#: ipam/forms/model_forms.py:67 ipam/forms/model_forms.py:84 +#: ipam/forms/model_forms.py:119 ipam/forms/model_forms.py:141 +#: ipam/forms/model_forms.py:166 ipam/forms/model_forms.py:233 +#: ipam/forms/model_forms.py:271 ipam/forms/model_forms.py:330 +#: ipam/forms/model_forms.py:631 netbox/navigation/menu.py:24 +#: templates/dcim/device_edit.html:87 templates/dcim/htmx/cable_edit.html:75 +#: templates/ipam/ipaddress_bulk_add.html:27 templates/ipam/vlan_edit.html:34 +#: virtualization/forms/model_forms.py:80 +#: virtualization/forms/model_forms.py:229 vpn/forms/bulk_edit.py:78 +#: vpn/forms/filtersets.py:48 vpn/forms/model_forms.py:63 +#: vpn/forms/model_forms.py:148 vpn/forms/model_forms.py:414 +#: wireless/forms/model_forms.py:58 wireless/forms/model_forms.py:174 msgid "Tenancy" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:215 -#: netbox/circuits/forms/model_forms.py:170 -#: netbox/dcim/forms/bulk_import.py:1317 netbox/dcim/forms/bulk_import.py:1335 +#: circuits/forms/bulk_edit.py:215 circuits/forms/model_forms.py:170 +#: dcim/forms/bulk_import.py:1348 dcim/forms/bulk_import.py:1366 msgid "Termination type" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:218 -#: netbox/circuits/forms/bulk_import.py:133 -#: netbox/circuits/forms/filtersets.py:226 -#: netbox/circuits/forms/model_forms.py:173 -#: netbox/templates/circuits/inc/circuit_termination.html:6 -#: netbox/templates/dcim/cable.html:68 netbox/templates/dcim/cable.html:72 -#: netbox/vpn/forms/bulk_import.py:100 netbox/vpn/forms/filtersets.py:82 +#: circuits/forms/bulk_edit.py:218 circuits/forms/bulk_import.py:133 +#: circuits/forms/filtersets.py:226 circuits/forms/model_forms.py:173 +#: templates/circuits/inc/circuit_termination.html:6 +#: templates/dcim/cable.html:68 templates/dcim/cable.html:72 +#: vpn/forms/bulk_import.py:100 vpn/forms/filtersets.py:82 msgid "Termination" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:226 +#: circuits/forms/bulk_edit.py:226 msgid "Port speed (Kbps)" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:230 +#: circuits/forms/bulk_edit.py:230 msgid "Upstream speed (Kbps)" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:233 netbox/dcim/forms/bulk_edit.py:969 -#: netbox/dcim/forms/bulk_edit.py:1333 netbox/dcim/forms/bulk_edit.py:1350 -#: netbox/dcim/forms/bulk_edit.py:1367 netbox/dcim/forms/bulk_edit.py:1385 -#: netbox/dcim/forms/bulk_edit.py:1480 netbox/dcim/forms/bulk_edit.py:1652 -#: netbox/dcim/forms/bulk_edit.py:1669 +#: circuits/forms/bulk_edit.py:233 dcim/forms/bulk_edit.py:1013 +#: dcim/forms/bulk_edit.py:1377 dcim/forms/bulk_edit.py:1394 +#: dcim/forms/bulk_edit.py:1411 dcim/forms/bulk_edit.py:1432 +#: dcim/forms/bulk_edit.py:1527 dcim/forms/bulk_edit.py:1699 +#: dcim/forms/bulk_edit.py:1716 msgid "Mark connected" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:243 -#: netbox/circuits/forms/model_forms.py:184 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:55 -#: netbox/templates/dcim/frontport.html:121 -#: netbox/templates/dcim/interface.html:250 -#: netbox/templates/dcim/rearport.html:111 +#: circuits/forms/bulk_edit.py:243 circuits/forms/model_forms.py:184 +#: templates/circuits/inc/circuit_termination_fields.html:55 +#: templates/dcim/frontport.html:121 templates/dcim/interface.html:250 +#: templates/dcim/rearport.html:111 msgid "Circuit Termination" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:245 -#: netbox/circuits/forms/model_forms.py:186 +#: circuits/forms/bulk_edit.py:245 circuits/forms/model_forms.py:186 msgid "Termination Details" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:289 -#: netbox/circuits/forms/bulk_import.py:188 -#: netbox/circuits/forms/filtersets.py:305 -#: netbox/circuits/tables/circuits.py:206 netbox/dcim/forms/model_forms.py:562 -#: netbox/templates/circuits/circuitgroupassignment.html:34 -#: netbox/templates/dcim/device.html:133 -#: netbox/templates/dcim/virtualchassis.html:68 -#: netbox/templates/dcim/virtualchassis_edit.html:60 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:26 -#: netbox/tenancy/forms/bulk_edit.py:148 netbox/tenancy/forms/filtersets.py:110 +#: circuits/forms/bulk_edit.py:289 circuits/forms/bulk_import.py:188 +#: circuits/forms/filtersets.py:305 circuits/tables/circuits.py:206 +#: dcim/forms/model_forms.py:656 +#: templates/circuits/circuitgroupassignment.html:34 +#: templates/dcim/device.html:133 templates/dcim/virtualchassis.html:68 +#: templates/dcim/virtualchassis_edit.html:60 +#: templates/ipam/inc/panels/fhrp_groups.html:26 tenancy/forms/bulk_edit.py:159 +#: tenancy/forms/filtersets.py:110 msgid "Priority" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:321 -#: netbox/circuits/forms/bulk_import.py:208 -#: netbox/circuits/forms/filtersets.py:159 -#: netbox/circuits/forms/filtersets.py:264 -#: netbox/circuits/forms/filtersets.py:354 -#: netbox/circuits/forms/filtersets.py:392 -#: netbox/circuits/forms/model_forms.py:325 -#: netbox/circuits/tables/virtual_circuits.py:51 -#: netbox/circuits/tables/virtual_circuits.py:99 +#: circuits/forms/bulk_edit.py:321 circuits/forms/bulk_import.py:208 +#: circuits/forms/filtersets.py:159 circuits/forms/filtersets.py:264 +#: circuits/forms/filtersets.py:354 circuits/forms/filtersets.py:392 +#: circuits/forms/model_forms.py:325 circuits/tables/virtual_circuits.py:51 +#: circuits/tables/virtual_circuits.py:99 msgid "Provider network" msgstr "" -#: netbox/circuits/forms/bulk_edit.py:365 -#: netbox/circuits/forms/bulk_import.py:254 -#: netbox/circuits/forms/filtersets.py:382 -#: netbox/circuits/forms/model_forms.py:365 netbox/dcim/forms/bulk_edit.py:361 -#: netbox/dcim/forms/bulk_edit.py:1280 netbox/dcim/forms/bulk_edit.py:1713 -#: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 -#: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 -#: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 -#: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 -#: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 -#: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 -#: netbox/extras/filtersets.py:552 netbox/ipam/forms/bulk_edit.py:245 -#: netbox/ipam/forms/bulk_edit.py:295 netbox/ipam/forms/bulk_edit.py:343 -#: netbox/ipam/forms/bulk_edit.py:495 netbox/ipam/forms/bulk_import.py:200 -#: netbox/ipam/forms/bulk_import.py:268 netbox/ipam/forms/bulk_import.py:304 -#: netbox/ipam/forms/bulk_import.py:494 netbox/ipam/forms/filtersets.py:247 -#: netbox/ipam/forms/filtersets.py:305 netbox/ipam/forms/filtersets.py:377 -#: netbox/ipam/forms/filtersets.py:564 netbox/ipam/forms/model_forms.py:194 -#: netbox/ipam/forms/model_forms.py:220 netbox/ipam/forms/model_forms.py:259 -#: netbox/ipam/forms/model_forms.py:685 netbox/ipam/tables/ip.py:209 -#: netbox/ipam/tables/ip.py:268 netbox/ipam/tables/ip.py:319 -#: netbox/ipam/tables/vlans.py:99 netbox/ipam/tables/vlans.py:211 -#: netbox/templates/circuits/virtualcircuittermination.html:42 -#: netbox/templates/dcim/device.html:182 -#: netbox/templates/dcim/inc/panels/inventory_items.html:20 -#: netbox/templates/dcim/interface.html:178 -#: netbox/templates/dcim/interface.html:280 -#: netbox/templates/dcim/inventoryitem.html:40 -#: netbox/templates/dcim/rack.html:49 netbox/templates/ipam/ipaddress.html:41 -#: netbox/templates/ipam/iprange.html:50 netbox/templates/ipam/prefix.html:73 -#: netbox/templates/ipam/role.html:19 netbox/templates/ipam/vlan.html:52 -#: netbox/templates/virtualization/virtualmachine.html:23 -#: netbox/templates/vpn/tunneltermination.html:17 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:20 -#: netbox/tenancy/forms/bulk_edit.py:143 netbox/tenancy/forms/filtersets.py:107 -#: netbox/tenancy/forms/model_forms.py:137 -#: netbox/tenancy/tables/contacts.py:102 -#: netbox/virtualization/forms/bulk_edit.py:127 -#: netbox/virtualization/forms/bulk_import.py:112 -#: netbox/virtualization/forms/filtersets.py:163 -#: netbox/virtualization/forms/model_forms.py:202 -#: netbox/virtualization/tables/virtualmachines.py:45 -#: netbox/vpn/forms/bulk_edit.py:87 netbox/vpn/forms/bulk_import.py:81 -#: netbox/vpn/forms/filtersets.py:90 netbox/vpn/forms/model_forms.py:79 -#: netbox/vpn/forms/model_forms.py:114 netbox/vpn/tables/tunnels.py:82 +#: circuits/forms/bulk_edit.py:365 circuits/forms/bulk_import.py:254 +#: circuits/forms/filtersets.py:382 circuits/forms/model_forms.py:365 +#: dcim/forms/bulk_edit.py:372 dcim/forms/bulk_edit.py:1324 +#: dcim/forms/bulk_edit.py:1760 dcim/forms/bulk_import.py:259 +#: dcim/forms/bulk_import.py:1137 dcim/forms/filtersets.py:369 +#: dcim/forms/filtersets.py:797 dcim/forms/filtersets.py:1622 +#: dcim/forms/model_forms.py:263 dcim/forms/model_forms.py:1215 +#: dcim/forms/model_forms.py:1684 dcim/forms/object_import.py:182 +#: dcim/tables/devices.py:179 dcim/tables/devices.py:847 +#: dcim/tables/devices.py:973 dcim/tables/devicetypes.py:311 +#: dcim/tables/racks.py:132 extras/filtersets.py:645 +#: ipam/forms/bulk_edit.py:245 ipam/forms/bulk_edit.py:295 +#: ipam/forms/bulk_edit.py:348 ipam/forms/bulk_edit.py:506 +#: ipam/forms/bulk_import.py:200 ipam/forms/bulk_import.py:268 +#: ipam/forms/bulk_import.py:304 ipam/forms/bulk_import.py:501 +#: ipam/forms/filtersets.py:247 ipam/forms/filtersets.py:305 +#: ipam/forms/filtersets.py:384 ipam/forms/filtersets.py:572 +#: ipam/forms/model_forms.py:194 ipam/forms/model_forms.py:220 +#: ipam/forms/model_forms.py:259 ipam/forms/model_forms.py:686 +#: ipam/tables/ip.py:210 ipam/tables/ip.py:269 ipam/tables/ip.py:325 +#: ipam/tables/vlans.py:101 ipam/tables/vlans.py:213 +#: templates/circuits/virtualcircuittermination.html:42 +#: templates/dcim/device.html:182 +#: templates/dcim/inc/panels/inventory_items.html:20 +#: templates/dcim/interface.html:178 templates/dcim/interface.html:280 +#: templates/dcim/inventoryitem.html:40 templates/dcim/rack.html:49 +#: templates/ipam/ipaddress.html:41 templates/ipam/iprange.html:57 +#: templates/ipam/prefix.html:73 templates/ipam/role.html:19 +#: templates/ipam/vlan.html:52 templates/virtualization/virtualmachine.html:23 +#: templates/vpn/tunneltermination.html:17 +#: templates/wireless/inc/wirelesslink_interface.html:20 +#: tenancy/forms/bulk_edit.py:154 tenancy/forms/filtersets.py:107 +#: tenancy/forms/model_forms.py:139 tenancy/tables/contacts.py:106 +#: virtualization/forms/bulk_edit.py:127 +#: virtualization/forms/bulk_import.py:112 +#: virtualization/forms/filtersets.py:163 +#: virtualization/forms/model_forms.py:202 +#: virtualization/tables/virtualmachines.py:45 vpn/forms/bulk_edit.py:87 +#: vpn/forms/bulk_import.py:81 vpn/forms/filtersets.py:90 +#: vpn/forms/model_forms.py:79 vpn/forms/model_forms.py:114 +#: vpn/tables/tunnels.py:82 msgid "Role" msgstr "" -#: netbox/circuits/forms/bulk_import.py:46 -#: netbox/circuits/forms/bulk_import.py:61 -#: netbox/circuits/forms/bulk_import.py:84 +#: circuits/forms/bulk_import.py:46 circuits/forms/bulk_import.py:61 +#: circuits/forms/bulk_import.py:84 msgid "Assigned provider" msgstr "" -#: netbox/circuits/forms/bulk_import.py:90 +#: circuits/forms/bulk_import.py:90 msgid "Assigned provider account" msgstr "" -#: netbox/circuits/forms/bulk_import.py:97 +#: circuits/forms/bulk_import.py:97 msgid "Type of circuit" msgstr "" -#: netbox/circuits/forms/bulk_import.py:102 -#: netbox/circuits/forms/bulk_import.py:229 netbox/dcim/forms/bulk_import.py:92 -#: netbox/dcim/forms/bulk_import.py:151 netbox/dcim/forms/bulk_import.py:252 -#: netbox/dcim/forms/bulk_import.py:534 netbox/dcim/forms/bulk_import.py:688 -#: netbox/dcim/forms/bulk_import.py:1139 netbox/dcim/forms/bulk_import.py:1510 -#: netbox/ipam/forms/bulk_import.py:197 netbox/ipam/forms/bulk_import.py:265 -#: netbox/ipam/forms/bulk_import.py:301 netbox/ipam/forms/bulk_import.py:491 -#: netbox/ipam/forms/bulk_import.py:504 -#: netbox/virtualization/forms/bulk_import.py:57 -#: netbox/virtualization/forms/bulk_import.py:88 -#: netbox/vpn/forms/bulk_import.py:39 netbox/wireless/forms/bulk_import.py:47 +#: circuits/forms/bulk_import.py:102 circuits/forms/bulk_import.py:229 +#: dcim/forms/bulk_import.py:93 dcim/forms/bulk_import.py:152 +#: dcim/forms/bulk_import.py:256 dcim/forms/bulk_import.py:565 +#: dcim/forms/bulk_import.py:719 dcim/forms/bulk_import.py:1170 +#: dcim/forms/bulk_import.py:1541 ipam/forms/bulk_import.py:197 +#: ipam/forms/bulk_import.py:265 ipam/forms/bulk_import.py:301 +#: ipam/forms/bulk_import.py:498 ipam/forms/bulk_import.py:511 +#: virtualization/forms/bulk_import.py:57 +#: virtualization/forms/bulk_import.py:88 vpn/forms/bulk_import.py:39 +#: vpn/forms/bulk_import.py:266 wireless/forms/bulk_import.py:47 msgid "Operational status" msgstr "" -#: netbox/circuits/forms/bulk_import.py:115 -#: netbox/circuits/forms/bulk_import.py:174 -#: netbox/circuits/forms/bulk_import.py:236 -#: netbox/dcim/forms/bulk_import.py:113 netbox/dcim/forms/bulk_import.py:158 -#: netbox/dcim/forms/bulk_import.py:362 netbox/dcim/forms/bulk_import.py:510 -#: netbox/dcim/forms/bulk_import.py:1360 netbox/dcim/forms/bulk_import.py:1505 -#: netbox/dcim/forms/bulk_import.py:1569 netbox/ipam/forms/bulk_import.py:45 -#: netbox/ipam/forms/bulk_import.py:74 netbox/ipam/forms/bulk_import.py:102 -#: netbox/ipam/forms/bulk_import.py:122 netbox/ipam/forms/bulk_import.py:142 -#: netbox/ipam/forms/bulk_import.py:171 netbox/ipam/forms/bulk_import.py:260 -#: netbox/ipam/forms/bulk_import.py:296 netbox/ipam/forms/bulk_import.py:486 -#: netbox/virtualization/forms/bulk_import.py:71 -#: netbox/virtualization/forms/bulk_import.py:125 -#: netbox/vpn/forms/bulk_import.py:63 netbox/wireless/forms/bulk_import.py:61 -#: netbox/wireless/forms/bulk_import.py:106 +#: circuits/forms/bulk_import.py:115 circuits/forms/bulk_import.py:174 +#: circuits/forms/bulk_import.py:236 dcim/forms/bulk_import.py:114 +#: dcim/forms/bulk_import.py:159 dcim/forms/bulk_import.py:366 +#: dcim/forms/bulk_import.py:541 dcim/forms/bulk_import.py:1391 +#: dcim/forms/bulk_import.py:1536 dcim/forms/bulk_import.py:1600 +#: ipam/forms/bulk_import.py:45 ipam/forms/bulk_import.py:74 +#: ipam/forms/bulk_import.py:102 ipam/forms/bulk_import.py:122 +#: ipam/forms/bulk_import.py:142 ipam/forms/bulk_import.py:171 +#: ipam/forms/bulk_import.py:260 ipam/forms/bulk_import.py:296 +#: ipam/forms/bulk_import.py:462 ipam/forms/bulk_import.py:493 +#: virtualization/forms/bulk_import.py:71 +#: virtualization/forms/bulk_import.py:125 vpn/forms/bulk_import.py:63 +#: wireless/forms/bulk_import.py:61 wireless/forms/bulk_import.py:106 msgid "Assigned tenant" msgstr "" -#: netbox/circuits/forms/bulk_import.py:139 +#: circuits/forms/bulk_import.py:139 msgid "Termination type (app & model)" msgstr "" -#: netbox/circuits/forms/bulk_import.py:151 -#: netbox/circuits/forms/bulk_import.py:164 +#: circuits/forms/bulk_import.py:151 circuits/forms/bulk_import.py:164 msgid "Termination ID" msgstr "" -#: netbox/circuits/forms/bulk_import.py:185 +#: circuits/forms/bulk_import.py:185 msgid "Circuit type (app & model)" msgstr "" -#: netbox/circuits/forms/bulk_import.py:211 +#: circuits/forms/bulk_import.py:211 msgid "The network to which this virtual circuit belongs" msgstr "" -#: netbox/circuits/forms/bulk_import.py:217 +#: circuits/forms/bulk_import.py:217 msgid "Assigned provider account (if any)" msgstr "" -#: netbox/circuits/forms/bulk_import.py:224 +#: circuits/forms/bulk_import.py:224 msgid "Type of virtual circuit" msgstr "" -#: netbox/circuits/forms/bulk_import.py:256 netbox/vpn/forms/bulk_import.py:83 +#: circuits/forms/bulk_import.py:256 vpn/forms/bulk_import.py:83 msgid "Operational role" msgstr "" -#: netbox/circuits/forms/bulk_import.py:259 -#: netbox/circuits/forms/model_forms.py:368 -#: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 -#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 -#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 -#: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 -#: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 -#: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 -#: netbox/ipam/tables/ip.py:324 netbox/ipam/tables/vlans.py:145 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:52 -#: netbox/templates/circuits/virtualcircuittermination.html:53 -#: netbox/templates/circuits/virtualcircuittermination.html:60 -#: netbox/templates/dcim/frontport.html:106 -#: netbox/templates/dcim/interface.html:27 -#: netbox/templates/dcim/interface.html:241 -#: netbox/templates/dcim/interface.html:367 -#: netbox/templates/dcim/rearport.html:102 -#: netbox/templates/virtualization/vminterface.html:18 -#: netbox/templates/vpn/tunneltermination.html:31 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:10 -#: netbox/templates/wireless/wirelesslink.html:10 -#: netbox/templates/wireless/wirelesslink.html:55 -#: netbox/virtualization/forms/model_forms.py:377 -#: netbox/vpn/forms/bulk_import.py:297 netbox/vpn/forms/model_forms.py:439 -#: netbox/vpn/forms/model_forms.py:448 netbox/wireless/forms/model_forms.py:116 -#: netbox/wireless/forms/model_forms.py:158 +#: circuits/forms/bulk_import.py:259 circuits/forms/model_forms.py:368 +#: circuits/tables/virtual_circuits.py:112 dcim/forms/bulk_import.py:1268 +#: dcim/forms/model_forms.py:1289 dcim/forms/model_forms.py:1558 +#: dcim/forms/model_forms.py:1725 dcim/forms/model_forms.py:1760 +#: dcim/forms/model_forms.py:1890 dcim/tables/connections.py:65 +#: dcim/tables/devices.py:1147 ipam/forms/bulk_import.py:324 +#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:299 +#: ipam/tables/fhrp.py:64 ipam/tables/ip.py:330 ipam/tables/vlans.py:147 +#: templates/circuits/inc/circuit_termination_fields.html:52 +#: templates/circuits/virtualcircuittermination.html:53 +#: templates/circuits/virtualcircuittermination.html:60 +#: templates/dcim/frontport.html:106 templates/dcim/interface.html:27 +#: templates/dcim/interface.html:241 templates/dcim/interface.html:367 +#: templates/dcim/rearport.html:102 +#: templates/virtualization/vminterface.html:18 +#: templates/vpn/tunneltermination.html:31 +#: templates/wireless/inc/wirelesslink_interface.html:10 +#: templates/wireless/wirelesslink.html:10 +#: templates/wireless/wirelesslink.html:55 +#: virtualization/forms/model_forms.py:377 vpn/forms/bulk_import.py:302 +#: vpn/forms/model_forms.py:439 vpn/forms/model_forms.py:448 +#: wireless/forms/model_forms.py:117 wireless/forms/model_forms.py:159 msgid "Interface" msgstr "" -#: netbox/circuits/forms/filtersets.py:38 -#: netbox/circuits/forms/filtersets.py:130 -#: netbox/circuits/forms/filtersets.py:188 -#: netbox/circuits/forms/filtersets.py:246 -#: netbox/circuits/tables/circuits.py:143 netbox/dcim/forms/bulk_edit.py:342 -#: netbox/dcim/forms/bulk_edit.py:450 netbox/dcim/forms/bulk_edit.py:691 -#: netbox/dcim/forms/bulk_edit.py:746 netbox/dcim/forms/bulk_edit.py:900 -#: netbox/dcim/forms/bulk_import.py:237 netbox/dcim/forms/bulk_import.py:339 -#: netbox/dcim/forms/bulk_import.py:573 netbox/dcim/forms/bulk_import.py:1454 -#: netbox/dcim/forms/bulk_import.py:1488 netbox/dcim/forms/filtersets.py:96 -#: netbox/dcim/forms/filtersets.py:323 netbox/dcim/forms/filtersets.py:357 -#: netbox/dcim/forms/filtersets.py:397 netbox/dcim/forms/filtersets.py:448 -#: netbox/dcim/forms/filtersets.py:720 netbox/dcim/forms/filtersets.py:763 -#: netbox/dcim/forms/filtersets.py:943 netbox/dcim/forms/filtersets.py:981 -#: netbox/dcim/forms/filtersets.py:1026 netbox/dcim/forms/filtersets.py:1055 -#: netbox/dcim/forms/filtersets.py:1075 netbox/dcim/forms/filtersets.py:1139 -#: netbox/dcim/forms/filtersets.py:1169 netbox/dcim/forms/filtersets.py:1178 -#: netbox/dcim/forms/filtersets.py:1289 netbox/dcim/forms/filtersets.py:1313 -#: netbox/dcim/forms/filtersets.py:1338 netbox/dcim/forms/filtersets.py:1357 -#: netbox/dcim/forms/filtersets.py:1385 netbox/dcim/forms/filtersets.py:1506 -#: netbox/dcim/forms/filtersets.py:1530 netbox/dcim/forms/filtersets.py:1554 -#: netbox/dcim/forms/filtersets.py:1572 netbox/dcim/forms/filtersets.py:1589 -#: netbox/dcim/forms/model_forms.py:184 netbox/dcim/forms/model_forms.py:248 -#: netbox/dcim/forms/model_forms.py:478 netbox/dcim/forms/model_forms.py:739 -#: netbox/dcim/tables/devices.py:167 netbox/dcim/tables/power.py:30 -#: netbox/dcim/tables/racks.py:117 netbox/dcim/tables/racks.py:211 -#: netbox/extras/filtersets.py:536 netbox/extras/forms/filtersets.py:327 -#: netbox/ipam/forms/filtersets.py:241 netbox/ipam/forms/filtersets.py:431 -#: netbox/ipam/forms/filtersets.py:454 netbox/ipam/forms/filtersets.py:521 -#: netbox/templates/dcim/device.html:26 -#: netbox/templates/dcim/device_edit.html:32 -#: netbox/templates/dcim/inc/cable_termination.html:12 -#: netbox/templates/dcim/location.html:26 -#: netbox/templates/dcim/powerpanel.html:26 netbox/templates/dcim/rack.html:24 -#: netbox/templates/dcim/rackreservation.html:32 -#: netbox/virtualization/forms/filtersets.py:80 -#: netbox/virtualization/forms/filtersets.py:106 -#: netbox/wireless/forms/filtersets.py:93 -#: netbox/wireless/forms/model_forms.py:90 -#: netbox/wireless/forms/model_forms.py:132 +#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:130 +#: circuits/forms/filtersets.py:188 circuits/forms/filtersets.py:246 +#: circuits/tables/circuits.py:143 dcim/forms/bulk_edit.py:353 +#: dcim/forms/bulk_edit.py:466 dcim/forms/bulk_edit.py:735 +#: dcim/forms/bulk_edit.py:790 dcim/forms/bulk_edit.py:944 +#: dcim/forms/bulk_import.py:241 dcim/forms/bulk_import.py:343 +#: dcim/forms/bulk_import.py:604 dcim/forms/bulk_import.py:1485 +#: dcim/forms/bulk_import.py:1519 dcim/forms/filtersets.py:97 +#: dcim/forms/filtersets.py:324 dcim/forms/filtersets.py:358 +#: dcim/forms/filtersets.py:398 dcim/forms/filtersets.py:449 +#: dcim/forms/filtersets.py:739 dcim/forms/filtersets.py:782 +#: dcim/forms/filtersets.py:962 dcim/forms/filtersets.py:1000 +#: dcim/forms/filtersets.py:1045 dcim/forms/filtersets.py:1074 +#: dcim/forms/filtersets.py:1094 dcim/forms/filtersets.py:1158 +#: dcim/forms/filtersets.py:1188 dcim/forms/filtersets.py:1197 +#: dcim/forms/filtersets.py:1308 dcim/forms/filtersets.py:1332 +#: dcim/forms/filtersets.py:1357 dcim/forms/filtersets.py:1376 +#: dcim/forms/filtersets.py:1409 dcim/forms/filtersets.py:1530 +#: dcim/forms/filtersets.py:1554 dcim/forms/filtersets.py:1578 +#: dcim/forms/filtersets.py:1596 dcim/forms/filtersets.py:1613 +#: dcim/forms/model_forms.py:190 dcim/forms/model_forms.py:255 +#: dcim/forms/model_forms.py:572 dcim/forms/model_forms.py:833 +#: dcim/tables/devices.py:167 dcim/tables/power.py:30 dcim/tables/racks.py:121 +#: dcim/tables/racks.py:220 extras/filtersets.py:629 +#: extras/forms/filtersets.py:362 ipam/forms/filtersets.py:241 +#: ipam/forms/filtersets.py:438 ipam/forms/filtersets.py:462 +#: ipam/forms/filtersets.py:529 templates/dcim/device.html:26 +#: templates/dcim/device_edit.html:32 +#: templates/dcim/inc/cable_termination.html:12 templates/dcim/location.html:26 +#: templates/dcim/powerpanel.html:26 templates/dcim/rack.html:24 +#: templates/dcim/rackreservation.html:32 virtualization/forms/filtersets.py:80 +#: virtualization/forms/filtersets.py:106 wireless/forms/filtersets.py:93 +#: wireless/forms/model_forms.py:91 wireless/forms/model_forms.py:133 msgid "Location" msgstr "" -#: netbox/circuits/forms/filtersets.py:40 -#: netbox/circuits/forms/filtersets.py:74 -#: netbox/circuits/forms/filtersets.py:132 netbox/dcim/forms/filtersets.py:145 -#: netbox/dcim/forms/filtersets.py:159 netbox/dcim/forms/filtersets.py:175 -#: netbox/dcim/forms/filtersets.py:207 netbox/dcim/forms/filtersets.py:329 -#: netbox/dcim/forms/filtersets.py:401 netbox/dcim/forms/filtersets.py:472 -#: netbox/dcim/forms/filtersets.py:724 netbox/dcim/forms/filtersets.py:1140 -#: netbox/ipam/forms/filtersets.py:103 netbox/ipam/forms/filtersets.py:183 -#: netbox/ipam/forms/filtersets.py:283 netbox/ipam/forms/filtersets.py:328 -#: netbox/ipam/forms/filtersets.py:613 netbox/netbox/navigation/menu.py:31 -#: netbox/netbox/navigation/menu.py:33 netbox/tenancy/forms/filtersets.py:42 -#: netbox/tenancy/tables/columns.py:55 netbox/tenancy/tables/contacts.py:25 -#: netbox/tenancy/views.py:19 netbox/virtualization/forms/filtersets.py:38 -#: netbox/virtualization/forms/filtersets.py:49 -#: netbox/virtualization/forms/filtersets.py:112 -#: netbox/vpn/forms/filtersets.py:37 netbox/vpn/forms/filtersets.py:49 -#: netbox/vpn/forms/filtersets.py:220 +#: circuits/forms/filtersets.py:40 circuits/forms/filtersets.py:74 +#: circuits/forms/filtersets.py:132 dcim/forms/filtersets.py:146 +#: dcim/forms/filtersets.py:160 dcim/forms/filtersets.py:176 +#: dcim/forms/filtersets.py:208 dcim/forms/filtersets.py:330 +#: dcim/forms/filtersets.py:402 dcim/forms/filtersets.py:473 +#: dcim/forms/filtersets.py:743 dcim/forms/filtersets.py:1159 +#: ipam/forms/filtersets.py:103 ipam/forms/filtersets.py:183 +#: ipam/forms/filtersets.py:283 ipam/forms/filtersets.py:335 +#: ipam/forms/filtersets.py:621 netbox/navigation/menu.py:31 +#: netbox/navigation/menu.py:33 netbox/views/generic/feature_views.py:262 +#: tenancy/forms/filtersets.py:42 tenancy/tables/columns.py:55 +#: tenancy/tables/contacts.py:25 virtualization/forms/filtersets.py:38 +#: virtualization/forms/filtersets.py:49 virtualization/forms/filtersets.py:112 +#: vpn/forms/filtersets.py:37 vpn/forms/filtersets.py:49 +#: vpn/forms/filtersets.py:220 msgid "Contacts" msgstr "" -#: netbox/circuits/forms/filtersets.py:45 -#: netbox/circuits/forms/filtersets.py:169 -#: netbox/circuits/forms/filtersets.py:231 -#: netbox/circuits/tables/circuits.py:138 netbox/dcim/forms/bulk_edit.py:116 -#: netbox/dcim/forms/bulk_edit.py:317 netbox/dcim/forms/bulk_edit.py:875 -#: netbox/dcim/forms/bulk_import.py:95 netbox/dcim/forms/filtersets.py:74 -#: netbox/dcim/forms/filtersets.py:186 netbox/dcim/forms/filtersets.py:212 -#: netbox/dcim/forms/filtersets.py:335 netbox/dcim/forms/filtersets.py:426 -#: netbox/dcim/forms/filtersets.py:740 netbox/dcim/forms/filtersets.py:959 -#: netbox/dcim/forms/filtersets.py:1032 netbox/dcim/forms/filtersets.py:1062 -#: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 -#: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 -#: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 -#: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 -#: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 -#: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 -#: netbox/templates/dcim/device.html:18 netbox/templates/dcim/rack.html:16 -#: netbox/templates/dcim/rackreservation.html:22 -#: netbox/templates/dcim/region.html:26 netbox/templates/dcim/site.html:31 -#: netbox/templates/ipam/vlan.html:16 -#: netbox/virtualization/forms/filtersets.py:60 -#: netbox/virtualization/forms/filtersets.py:139 -#: netbox/virtualization/forms/model_forms.py:92 -#: netbox/vpn/forms/filtersets.py:263 netbox/wireless/forms/filtersets.py:73 +#: circuits/forms/filtersets.py:45 circuits/forms/filtersets.py:169 +#: circuits/forms/filtersets.py:231 circuits/tables/circuits.py:138 +#: dcim/forms/bulk_edit.py:121 dcim/forms/bulk_edit.py:328 +#: dcim/forms/bulk_edit.py:919 dcim/forms/bulk_import.py:96 +#: dcim/forms/filtersets.py:75 dcim/forms/filtersets.py:187 +#: dcim/forms/filtersets.py:213 dcim/forms/filtersets.py:336 +#: dcim/forms/filtersets.py:427 dcim/forms/filtersets.py:759 +#: dcim/forms/filtersets.py:978 dcim/forms/filtersets.py:1051 +#: dcim/forms/filtersets.py:1081 dcim/forms/filtersets.py:1165 +#: dcim/forms/filtersets.py:1204 dcim/forms/filtersets.py:1697 +#: dcim/forms/filtersets.py:1721 dcim/forms/filtersets.py:1745 +#: dcim/forms/model_forms.py:119 dcim/forms/object_create.py:379 +#: dcim/tables/devices.py:153 dcim/tables/sites.py:91 extras/filtersets.py:596 +#: ipam/forms/bulk_edit.py:469 ipam/forms/filtersets.py:226 +#: ipam/forms/filtersets.py:447 ipam/forms/filtersets.py:538 +#: templates/dcim/device.html:18 templates/dcim/rack.html:16 +#: templates/dcim/rackreservation.html:22 templates/dcim/region.html:26 +#: templates/dcim/site.html:31 templates/ipam/vlan.html:16 +#: virtualization/forms/filtersets.py:60 virtualization/forms/filtersets.py:139 +#: virtualization/forms/model_forms.py:92 vpn/forms/filtersets.py:268 +#: wireless/forms/filtersets.py:73 msgid "Region" msgstr "" -#: netbox/circuits/forms/filtersets.py:50 -#: netbox/circuits/forms/filtersets.py:174 -#: netbox/circuits/forms/filtersets.py:236 netbox/dcim/forms/bulk_edit.py:325 -#: netbox/dcim/forms/bulk_edit.py:883 netbox/dcim/forms/filtersets.py:79 -#: netbox/dcim/forms/filtersets.py:191 netbox/dcim/forms/filtersets.py:217 -#: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 -#: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 -#: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 -#: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 -#: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 -#: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 -#: netbox/virtualization/forms/filtersets.py:65 -#: netbox/virtualization/forms/filtersets.py:144 -#: netbox/virtualization/forms/model_forms.py:98 -#: netbox/wireless/forms/filtersets.py:78 +#: circuits/forms/filtersets.py:50 circuits/forms/filtersets.py:174 +#: circuits/forms/filtersets.py:236 dcim/forms/bulk_edit.py:336 +#: dcim/forms/bulk_edit.py:927 dcim/forms/filtersets.py:80 +#: dcim/forms/filtersets.py:192 dcim/forms/filtersets.py:218 +#: dcim/forms/filtersets.py:349 dcim/forms/filtersets.py:432 +#: dcim/forms/filtersets.py:764 dcim/forms/filtersets.py:983 +#: dcim/forms/filtersets.py:1056 dcim/forms/filtersets.py:1170 +#: dcim/forms/filtersets.py:1209 dcim/forms/object_create.py:387 +#: extras/filtersets.py:613 ipam/forms/bulk_edit.py:474 +#: ipam/forms/filtersets.py:156 ipam/forms/filtersets.py:231 +#: ipam/forms/filtersets.py:452 ipam/forms/filtersets.py:543 +#: virtualization/forms/filtersets.py:65 virtualization/forms/filtersets.py:144 +#: virtualization/forms/model_forms.py:98 wireless/forms/filtersets.py:78 msgid "Site group" msgstr "" -#: netbox/circuits/forms/filtersets.py:82 netbox/circuits/tables/circuits.py:62 -#: netbox/circuits/tables/providers.py:64 -#: netbox/circuits/tables/virtual_circuits.py:55 -#: netbox/circuits/tables/virtual_circuits.py:103 -#: netbox/templates/circuits/circuit.html:22 -#: netbox/templates/circuits/provideraccount.html:24 +#: circuits/forms/filtersets.py:82 circuits/tables/circuits.py:62 +#: circuits/tables/providers.py:64 circuits/tables/virtual_circuits.py:55 +#: circuits/tables/virtual_circuits.py:103 templates/circuits/circuit.html:22 +#: templates/circuits/provideraccount.html:24 msgid "Account" msgstr "" -#: netbox/circuits/forms/filtersets.py:254 +#: circuits/forms/filtersets.py:254 msgid "Term Side" msgstr "" -#: netbox/circuits/forms/filtersets.py:287 netbox/dcim/forms/bulk_edit.py:1572 -#: netbox/extras/forms/model_forms.py:596 netbox/ipam/forms/filtersets.py:145 -#: netbox/ipam/forms/filtersets.py:612 netbox/ipam/forms/model_forms.py:337 -#: netbox/templates/dcim/macaddress.html:25 -#: netbox/templates/extras/configcontext.html:60 -#: netbox/templates/ipam/ipaddress.html:59 -#: netbox/templates/ipam/vlan_edit.html:42 -#: netbox/tenancy/forms/filtersets.py:87 netbox/users/forms/model_forms.py:314 +#: circuits/forms/filtersets.py:287 dcim/forms/bulk_edit.py:1619 +#: extras/forms/model_forms.py:664 ipam/forms/filtersets.py:145 +#: ipam/forms/filtersets.py:620 ipam/forms/model_forms.py:337 +#: templates/dcim/macaddress.html:25 templates/extras/configcontext.html:60 +#: templates/ipam/ipaddress.html:59 templates/ipam/vlan_edit.html:42 +#: tenancy/forms/filtersets.py:87 users/forms/model_forms.py:314 msgid "Assignment" msgstr "" -#: netbox/circuits/forms/filtersets.py:302 -#: netbox/circuits/forms/model_forms.py:252 -#: netbox/circuits/tables/circuits.py:190 netbox/dcim/forms/bulk_edit.py:121 -#: netbox/dcim/forms/bulk_import.py:102 netbox/dcim/forms/model_forms.py:120 -#: netbox/dcim/tables/sites.py:89 netbox/extras/forms/filtersets.py:489 -#: netbox/ipam/filtersets.py:994 netbox/ipam/forms/bulk_edit.py:477 -#: netbox/ipam/forms/bulk_import.py:475 netbox/ipam/forms/model_forms.py:570 -#: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:91 -#: netbox/ipam/tables/vlans.py:202 -#: netbox/templates/circuits/circuitgroupassignment.html:22 -#: netbox/templates/dcim/interface.html:341 netbox/templates/dcim/site.html:37 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:23 -#: netbox/templates/ipam/vlan.html:27 netbox/templates/tenancy/contact.html:21 -#: netbox/templates/tenancy/tenant.html:20 netbox/templates/users/group.html:6 -#: netbox/templates/users/group.html:14 -#: netbox/templates/virtualization/cluster.html:29 -#: netbox/templates/vpn/tunnel.html:29 -#: netbox/templates/wireless/wirelesslan.html:18 -#: netbox/tenancy/forms/bulk_edit.py:43 netbox/tenancy/forms/bulk_edit.py:94 -#: netbox/tenancy/forms/bulk_import.py:40 -#: netbox/tenancy/forms/bulk_import.py:81 netbox/tenancy/forms/filtersets.py:48 -#: netbox/tenancy/forms/filtersets.py:78 netbox/tenancy/forms/filtersets.py:97 -#: netbox/tenancy/forms/model_forms.py:45 -#: netbox/tenancy/forms/model_forms.py:97 -#: netbox/tenancy/forms/model_forms.py:122 netbox/tenancy/tables/contacts.py:60 -#: netbox/tenancy/tables/contacts.py:107 netbox/tenancy/tables/tenants.py:42 -#: netbox/users/filtersets.py:62 netbox/users/filtersets.py:185 -#: netbox/users/forms/filtersets.py:31 netbox/users/forms/filtersets.py:37 -#: netbox/users/forms/filtersets.py:79 -#: netbox/virtualization/forms/bulk_edit.py:66 -#: netbox/virtualization/forms/bulk_import.py:48 -#: netbox/virtualization/forms/filtersets.py:91 -#: netbox/virtualization/forms/model_forms.py:70 -#: netbox/virtualization/tables/clusters.py:70 -#: netbox/vpn/forms/bulk_edit.py:112 netbox/vpn/forms/bulk_import.py:158 -#: netbox/vpn/forms/filtersets.py:121 netbox/vpn/tables/crypto.py:31 -#: netbox/vpn/tables/tunnels.py:44 netbox/wireless/forms/bulk_edit.py:50 -#: netbox/wireless/forms/bulk_import.py:38 -#: netbox/wireless/forms/filtersets.py:49 -#: netbox/wireless/forms/model_forms.py:41 -#: netbox/wireless/tables/wirelesslan.py:48 +#: circuits/forms/filtersets.py:302 circuits/forms/model_forms.py:252 +#: circuits/tables/circuits.py:190 dcim/forms/bulk_edit.py:126 +#: dcim/forms/bulk_import.py:103 dcim/forms/model_forms.py:125 +#: dcim/tables/sites.py:95 extras/forms/filtersets.py:544 +#: ipam/filtersets.py:994 ipam/forms/bulk_edit.py:488 +#: ipam/forms/bulk_import.py:482 ipam/forms/model_forms.py:570 +#: ipam/tables/fhrp.py:67 ipam/tables/vlans.py:93 ipam/tables/vlans.py:204 +#: templates/circuits/circuitgroupassignment.html:22 +#: templates/dcim/interface.html:341 templates/dcim/site.html:37 +#: templates/ipam/inc/panels/fhrp_groups.html:23 templates/ipam/vlan.html:27 +#: templates/tenancy/tenant.html:20 templates/users/group.html:6 +#: templates/users/group.html:14 templates/virtualization/cluster.html:29 +#: templates/vpn/tunnel.html:29 templates/wireless/wirelesslan.html:18 +#: tenancy/forms/bulk_edit.py:44 tenancy/forms/bulk_import.py:40 +#: tenancy/forms/filtersets.py:48 tenancy/forms/filtersets.py:97 +#: tenancy/forms/model_forms.py:46 tenancy/forms/model_forms.py:124 +#: tenancy/tables/contacts.py:111 tenancy/tables/tenants.py:46 +#: users/filtersets.py:62 users/filtersets.py:185 users/forms/filtersets.py:31 +#: users/forms/filtersets.py:37 users/forms/filtersets.py:79 +#: virtualization/forms/bulk_edit.py:66 virtualization/forms/bulk_import.py:48 +#: virtualization/forms/filtersets.py:91 virtualization/forms/model_forms.py:70 +#: virtualization/tables/clusters.py:70 vpn/forms/bulk_edit.py:112 +#: vpn/forms/bulk_import.py:158 vpn/forms/filtersets.py:121 +#: vpn/tables/crypto.py:31 vpn/tables/tunnels.py:44 +#: wireless/forms/bulk_edit.py:51 wireless/forms/bulk_import.py:38 +#: wireless/forms/filtersets.py:49 wireless/forms/model_forms.py:42 +#: wireless/tables/wirelesslan.py:48 msgid "Group" msgstr "" -#: netbox/circuits/forms/model_forms.py:239 -#: netbox/templates/circuits/circuitgroup.html:25 +#: circuits/forms/model_forms.py:239 templates/circuits/circuitgroup.html:25 msgid "Circuit Group" msgstr "" -#: netbox/circuits/forms/model_forms.py:259 +#: circuits/forms/model_forms.py:259 msgid "Circuit type" msgstr "" -#: netbox/circuits/forms/model_forms.py:270 +#: circuits/forms/model_forms.py:270 msgid "Group Assignment" msgstr "" -#: netbox/circuits/models/base.py:18 netbox/dcim/models/cables.py:67 -#: netbox/dcim/models/device_component_templates.py:531 -#: netbox/dcim/models/device_component_templates.py:631 -#: netbox/dcim/models/device_components.py:479 -#: netbox/dcim/models/device_components.py:1029 -#: netbox/dcim/models/device_components.py:1100 -#: netbox/dcim/models/device_components.py:1246 -#: netbox/dcim/models/devices.py:478 netbox/dcim/models/racks.py:221 -#: netbox/extras/models/tags.py:28 +#: circuits/models/base.py:18 dcim/models/cables.py:67 +#: dcim/models/device_component_templates.py:531 +#: dcim/models/device_component_templates.py:631 +#: dcim/models/device_components.py:485 dcim/models/device_components.py:1038 +#: dcim/models/device_components.py:1109 dcim/models/device_components.py:1255 +#: dcim/models/devices.py:381 dcim/models/racks.py:227 extras/models/tags.py:29 msgid "color" msgstr "" -#: netbox/circuits/models/circuits.py:34 +#: circuits/models/circuits.py:34 msgid "circuit type" msgstr "" -#: netbox/circuits/models/circuits.py:35 +#: circuits/models/circuits.py:35 msgid "circuit types" msgstr "" -#: netbox/circuits/models/circuits.py:46 -#: netbox/circuits/models/virtual_circuits.py:38 +#: circuits/models/circuits.py:46 circuits/models/virtual_circuits.py:38 msgid "circuit ID" msgstr "" -#: netbox/circuits/models/circuits.py:47 -#: netbox/circuits/models/virtual_circuits.py:39 +#: circuits/models/circuits.py:47 circuits/models/virtual_circuits.py:39 msgid "Unique circuit ID" msgstr "" -#: netbox/circuits/models/circuits.py:67 -#: netbox/circuits/models/virtual_circuits.py:59 netbox/core/models/data.py:52 -#: netbox/core/models/jobs.py:87 netbox/dcim/models/cables.py:49 -#: netbox/dcim/models/device_components.py:1286 -#: netbox/dcim/models/devices.py:645 netbox/dcim/models/devices.py:1181 -#: netbox/dcim/models/devices.py:1409 netbox/dcim/models/power.py:94 -#: netbox/dcim/models/racks.py:288 netbox/dcim/models/sites.py:154 -#: netbox/dcim/models/sites.py:270 netbox/ipam/models/ip.py:237 -#: netbox/ipam/models/ip.py:508 netbox/ipam/models/ip.py:729 -#: netbox/ipam/models/vlans.py:211 netbox/virtualization/models/clusters.py:70 -#: netbox/virtualization/models/virtualmachines.py:79 -#: netbox/vpn/models/tunnels.py:38 netbox/wireless/models.py:95 -#: netbox/wireless/models.py:156 +#: circuits/models/circuits.py:67 circuits/models/virtual_circuits.py:59 +#: core/models/data.py:52 core/models/jobs.py:87 dcim/models/cables.py:49 +#: dcim/models/device_components.py:456 dcim/models/device_components.py:1294 +#: dcim/models/devices.py:533 dcim/models/devices.py:1138 +#: dcim/models/modules.py:221 dcim/models/power.py:94 dcim/models/racks.py:294 +#: dcim/models/sites.py:154 dcim/models/sites.py:270 ipam/models/ip.py:237 +#: ipam/models/ip.py:511 ipam/models/ip.py:740 ipam/models/vlans.py:217 +#: virtualization/models/clusters.py:70 +#: virtualization/models/virtualmachines.py:79 vpn/models/l2vpn.py:36 +#: vpn/models/tunnels.py:38 wireless/models.py:95 wireless/models.py:148 msgid "status" msgstr "" -#: netbox/circuits/models/circuits.py:82 netbox/templates/core/plugin.html:20 +#: circuits/models/circuits.py:82 templates/core/plugin.html:20 msgid "installed" msgstr "" -#: netbox/circuits/models/circuits.py:87 +#: circuits/models/circuits.py:87 msgid "terminates" msgstr "" -#: netbox/circuits/models/circuits.py:92 +#: circuits/models/circuits.py:92 msgid "commit rate (Kbps)" msgstr "" -#: netbox/circuits/models/circuits.py:93 +#: circuits/models/circuits.py:93 msgid "Committed rate" msgstr "" -#: netbox/circuits/models/circuits.py:142 +#: circuits/models/circuits.py:142 msgid "circuit" msgstr "" -#: netbox/circuits/models/circuits.py:143 +#: circuits/models/circuits.py:143 msgid "circuits" msgstr "" -#: netbox/circuits/models/circuits.py:172 +#: circuits/models/circuits.py:172 msgid "circuit group" msgstr "" -#: netbox/circuits/models/circuits.py:173 +#: circuits/models/circuits.py:173 msgid "circuit groups" msgstr "" -#: netbox/circuits/models/circuits.py:190 +#: circuits/models/circuits.py:189 msgid "member ID" msgstr "" -#: netbox/circuits/models/circuits.py:202 netbox/ipam/models/fhrp.py:90 -#: netbox/tenancy/models/contacts.py:126 +#: circuits/models/circuits.py:201 ipam/models/fhrp.py:96 +#: tenancy/models/contacts.py:133 msgid "priority" msgstr "" -#: netbox/circuits/models/circuits.py:220 +#: circuits/models/circuits.py:219 msgid "Circuit group assignment" msgstr "" -#: netbox/circuits/models/circuits.py:221 +#: circuits/models/circuits.py:220 msgid "Circuit group assignments" msgstr "" -#: netbox/circuits/models/circuits.py:247 +#: circuits/models/circuits.py:246 msgid "termination side" msgstr "" -#: netbox/circuits/models/circuits.py:266 +#: circuits/models/circuits.py:264 msgid "port speed (Kbps)" msgstr "" -#: netbox/circuits/models/circuits.py:269 +#: circuits/models/circuits.py:267 msgid "Physical circuit speed" msgstr "" -#: netbox/circuits/models/circuits.py:274 +#: circuits/models/circuits.py:272 msgid "upstream speed (Kbps)" msgstr "" -#: netbox/circuits/models/circuits.py:275 +#: circuits/models/circuits.py:273 msgid "Upstream speed, if different from port speed" msgstr "" -#: netbox/circuits/models/circuits.py:280 +#: circuits/models/circuits.py:278 msgid "cross-connect ID" msgstr "" -#: netbox/circuits/models/circuits.py:281 +#: circuits/models/circuits.py:279 msgid "ID of the local cross-connect" msgstr "" -#: netbox/circuits/models/circuits.py:286 +#: circuits/models/circuits.py:284 msgid "patch panel/port(s)" msgstr "" -#: netbox/circuits/models/circuits.py:287 +#: circuits/models/circuits.py:285 msgid "Patch panel ID and port number(s)" msgstr "" -#: netbox/circuits/models/circuits.py:290 -#: netbox/circuits/models/virtual_circuits.py:144 -#: netbox/dcim/models/device_component_templates.py:57 -#: netbox/dcim/models/device_components.py:63 netbox/dcim/models/racks.py:681 -#: netbox/extras/models/configs.py:45 netbox/extras/models/configs.py:219 -#: netbox/extras/models/customfields.py:127 netbox/extras/models/models.py:61 -#: netbox/extras/models/models.py:158 netbox/extras/models/models.py:396 -#: netbox/extras/models/models.py:511 netbox/extras/models/notifications.py:131 -#: netbox/extras/models/staging.py:32 netbox/extras/models/tags.py:32 -#: netbox/ipam/models/vlans.py:367 netbox/netbox/models/__init__.py:115 -#: netbox/netbox/models/__init__.py:150 netbox/netbox/models/__init__.py:196 -#: netbox/users/models/permissions.py:24 netbox/users/models/tokens.py:57 -#: netbox/users/models/users.py:33 -#: netbox/virtualization/models/virtualmachines.py:276 +#: circuits/models/circuits.py:288 circuits/models/virtual_circuits.py:144 +#: dcim/models/device_component_templates.py:57 +#: dcim/models/device_components.py:63 dcim/models/racks.py:688 +#: extras/models/configs.py:42 extras/models/configs.py:218 +#: extras/models/customfields.py:127 extras/models/models.py:63 +#: extras/models/models.py:160 extras/models/models.py:398 +#: extras/models/models.py:469 extras/models/models.py:548 +#: extras/models/notifications.py:131 extras/models/tags.py:33 +#: ipam/models/vlans.py:373 netbox/models/__init__.py:115 +#: netbox/models/__init__.py:150 netbox/models/__init__.py:200 +#: users/models/permissions.py:23 users/models/tokens.py:57 +#: users/models/users.py:33 virtualization/models/virtualmachines.py:281 msgid "description" msgstr "" -#: netbox/circuits/models/circuits.py:340 +#: circuits/models/circuits.py:338 msgid "circuit termination" msgstr "" -#: netbox/circuits/models/circuits.py:341 +#: circuits/models/circuits.py:339 msgid "circuit terminations" msgstr "" -#: netbox/circuits/models/circuits.py:353 +#: circuits/models/circuits.py:351 msgid "A circuit termination must attach to a terminating object." msgstr "" -#: netbox/circuits/models/providers.py:21 -#: netbox/circuits/models/providers.py:63 -#: netbox/circuits/models/providers.py:98 netbox/core/models/data.py:39 -#: netbox/core/models/jobs.py:48 -#: netbox/dcim/models/device_component_templates.py:43 -#: netbox/dcim/models/device_components.py:52 netbox/dcim/models/devices.py:589 -#: netbox/dcim/models/devices.py:1341 netbox/dcim/models/devices.py:1404 -#: netbox/dcim/models/power.py:38 netbox/dcim/models/power.py:89 -#: netbox/dcim/models/racks.py:257 netbox/dcim/models/sites.py:142 -#: netbox/extras/models/configs.py:36 netbox/extras/models/configs.py:215 -#: netbox/extras/models/customfields.py:94 netbox/extras/models/models.py:56 -#: netbox/extras/models/models.py:153 netbox/extras/models/models.py:296 -#: netbox/extras/models/models.py:392 netbox/extras/models/models.py:501 -#: netbox/extras/models/models.py:596 netbox/extras/models/notifications.py:126 -#: netbox/extras/models/scripts.py:30 netbox/extras/models/staging.py:27 -#: netbox/ipam/models/asns.py:17 netbox/ipam/models/fhrp.py:24 -#: netbox/ipam/models/services.py:51 netbox/ipam/models/services.py:84 -#: netbox/ipam/models/vlans.py:38 netbox/ipam/models/vlans.py:200 -#: netbox/ipam/models/vlans.py:346 netbox/ipam/models/vrfs.py:20 -#: netbox/ipam/models/vrfs.py:75 netbox/netbox/models/__init__.py:142 -#: netbox/netbox/models/__init__.py:186 netbox/tenancy/models/contacts.py:58 -#: netbox/tenancy/models/tenants.py:19 netbox/tenancy/models/tenants.py:42 -#: netbox/users/models/permissions.py:20 netbox/users/models/users.py:28 -#: netbox/virtualization/models/clusters.py:52 -#: netbox/virtualization/models/virtualmachines.py:71 -#: netbox/virtualization/models/virtualmachines.py:271 -#: netbox/virtualization/models/virtualmachines.py:305 -#: netbox/vpn/models/crypto.py:23 netbox/vpn/models/crypto.py:69 -#: netbox/vpn/models/crypto.py:128 netbox/vpn/models/crypto.py:180 -#: netbox/vpn/models/crypto.py:216 netbox/vpn/models/l2vpn.py:21 -#: netbox/vpn/models/tunnels.py:32 netbox/wireless/models.py:53 +#: circuits/models/providers.py:21 circuits/models/providers.py:63 +#: circuits/models/providers.py:98 core/models/data.py:39 +#: core/models/jobs.py:48 dcim/models/device_component_templates.py:43 +#: dcim/models/device_components.py:52 dcim/models/devices.py:477 +#: dcim/models/devices.py:1070 dcim/models/devices.py:1133 +#: dcim/models/modules.py:32 dcim/models/power.py:38 dcim/models/power.py:89 +#: dcim/models/racks.py:263 dcim/models/sites.py:142 +#: extras/models/configs.py:33 extras/models/configs.py:214 +#: extras/models/customfields.py:94 extras/models/models.py:58 +#: extras/models/models.py:155 extras/models/models.py:298 +#: extras/models/models.py:394 extras/models/models.py:459 +#: extras/models/models.py:544 extras/models/models.py:669 +#: extras/models/notifications.py:126 extras/models/scripts.py:30 +#: ipam/models/asns.py:17 ipam/models/fhrp.py:24 ipam/models/services.py:51 +#: ipam/models/services.py:80 ipam/models/vlans.py:38 ipam/models/vlans.py:206 +#: ipam/models/vlans.py:352 ipam/models/vrfs.py:20 ipam/models/vrfs.py:75 +#: netbox/models/__init__.py:142 netbox/models/__init__.py:190 +#: tenancy/models/contacts.py:59 tenancy/models/tenants.py:19 +#: tenancy/models/tenants.py:42 users/models/permissions.py:19 +#: users/models/users.py:28 virtualization/models/clusters.py:52 +#: virtualization/models/virtualmachines.py:71 +#: virtualization/models/virtualmachines.py:276 +#: virtualization/models/virtualmachines.py:310 vpn/models/crypto.py:23 +#: vpn/models/crypto.py:69 vpn/models/crypto.py:128 vpn/models/crypto.py:180 +#: vpn/models/crypto.py:216 vpn/models/l2vpn.py:20 vpn/models/tunnels.py:32 +#: wireless/models.py:53 msgid "name" msgstr "" -#: netbox/circuits/models/providers.py:24 +#: circuits/models/providers.py:24 msgid "Full name of the provider" msgstr "" -#: netbox/circuits/models/providers.py:28 netbox/dcim/models/devices.py:88 -#: netbox/dcim/models/racks.py:137 netbox/dcim/models/sites.py:149 -#: netbox/extras/models/models.py:506 netbox/ipam/models/asns.py:23 -#: netbox/ipam/models/vlans.py:43 netbox/netbox/models/__init__.py:146 -#: netbox/netbox/models/__init__.py:191 netbox/tenancy/models/tenants.py:25 -#: netbox/tenancy/models/tenants.py:47 netbox/vpn/models/l2vpn.py:27 -#: netbox/wireless/models.py:59 +#: circuits/models/providers.py:28 dcim/models/devices.py:88 +#: dcim/models/racks.py:143 dcim/models/sites.py:149 +#: extras/models/models.py:464 ipam/models/asns.py:23 ipam/models/vlans.py:43 +#: netbox/models/__init__.py:146 netbox/models/__init__.py:195 +#: tenancy/models/tenants.py:25 tenancy/models/tenants.py:47 +#: vpn/models/l2vpn.py:26 wireless/models.py:59 msgid "slug" msgstr "" -#: netbox/circuits/models/providers.py:42 +#: circuits/models/providers.py:42 msgid "provider" msgstr "" -#: netbox/circuits/models/providers.py:43 +#: circuits/models/providers.py:43 msgid "providers" msgstr "" -#: netbox/circuits/models/providers.py:60 +#: circuits/models/providers.py:60 msgid "account ID" msgstr "" -#: netbox/circuits/models/providers.py:83 +#: circuits/models/providers.py:83 msgid "provider account" msgstr "" -#: netbox/circuits/models/providers.py:84 +#: circuits/models/providers.py:84 msgid "provider accounts" msgstr "" -#: netbox/circuits/models/providers.py:110 +#: circuits/models/providers.py:110 msgid "service ID" msgstr "" -#: netbox/circuits/models/providers.py:121 +#: circuits/models/providers.py:121 msgid "provider network" msgstr "" -#: netbox/circuits/models/providers.py:122 +#: circuits/models/providers.py:122 msgid "provider networks" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:28 +#: circuits/models/virtual_circuits.py:28 msgid "virtual circuit type" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:29 +#: circuits/models/virtual_circuits.py:29 msgid "virtual circuit types" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:99 +#: circuits/models/virtual_circuits.py:99 msgid "virtual circuit" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:100 +#: circuits/models/virtual_circuits.py:100 msgid "virtual circuits" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:133 netbox/ipam/models/ip.py:194 -#: netbox/ipam/models/ip.py:736 netbox/vpn/models/tunnels.py:109 +#: circuits/models/virtual_circuits.py:133 ipam/models/ip.py:194 +#: ipam/models/ip.py:747 vpn/models/tunnels.py:109 msgid "role" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:151 +#: circuits/models/virtual_circuits.py:151 msgid "virtual circuit termination" msgstr "" -#: netbox/circuits/models/virtual_circuits.py:152 +#: circuits/models/virtual_circuits.py:152 msgid "virtual circuit terminations" msgstr "" -#: netbox/circuits/tables/circuits.py:30 netbox/circuits/tables/circuits.py:167 -#: netbox/circuits/tables/providers.py:18 -#: netbox/circuits/tables/providers.py:67 -#: netbox/circuits/tables/providers.py:97 -#: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 -#: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 -#: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 -#: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 -#: netbox/dcim/tables/devices.py:145 netbox/dcim/tables/devices.py:299 -#: netbox/dcim/tables/devices.py:402 netbox/dcim/tables/devices.py:443 -#: netbox/dcim/tables/devices.py:491 netbox/dcim/tables/devices.py:540 -#: netbox/dcim/tables/devices.py:561 netbox/dcim/tables/devices.py:681 -#: netbox/dcim/tables/devices.py:764 netbox/dcim/tables/devices.py:810 -#: netbox/dcim/tables/devices.py:872 netbox/dcim/tables/devices.py:941 -#: netbox/dcim/tables/devices.py:1006 netbox/dcim/tables/devices.py:1025 -#: netbox/dcim/tables/devices.py:1054 netbox/dcim/tables/devices.py:1084 -#: netbox/dcim/tables/devicetypes.py:31 netbox/dcim/tables/devicetypes.py:227 -#: netbox/dcim/tables/power.py:22 netbox/dcim/tables/power.py:62 -#: netbox/dcim/tables/racks.py:24 netbox/dcim/tables/racks.py:113 -#: netbox/dcim/tables/sites.py:24 netbox/dcim/tables/sites.py:51 -#: netbox/dcim/tables/sites.py:78 netbox/dcim/tables/sites.py:129 -#: netbox/extras/forms/filtersets.py:218 netbox/extras/tables/tables.py:61 -#: netbox/extras/tables/tables.py:125 netbox/extras/tables/tables.py:158 -#: netbox/extras/tables/tables.py:183 netbox/extras/tables/tables.py:249 -#: netbox/extras/tables/tables.py:364 netbox/extras/tables/tables.py:381 -#: netbox/extras/tables/tables.py:404 netbox/extras/tables/tables.py:442 -#: netbox/extras/tables/tables.py:494 netbox/extras/tables/tables.py:520 -#: netbox/ipam/forms/bulk_edit.py:391 netbox/ipam/forms/filtersets.py:403 -#: netbox/ipam/forms/filtersets.py:488 netbox/ipam/tables/asn.py:16 -#: netbox/ipam/tables/ip.py:31 netbox/ipam/tables/ip.py:106 -#: netbox/ipam/tables/services.py:15 netbox/ipam/tables/services.py:40 -#: netbox/ipam/tables/vlans.py:33 netbox/ipam/tables/vlans.py:83 -#: netbox/ipam/tables/vlans.py:231 netbox/ipam/tables/vrfs.py:26 -#: netbox/ipam/tables/vrfs.py:68 netbox/templates/circuits/circuitgroup.html:28 -#: netbox/templates/circuits/circuittype.html:22 -#: netbox/templates/circuits/provideraccount.html:28 -#: netbox/templates/circuits/providernetwork.html:24 -#: netbox/templates/circuits/virtualcircuittype.html:22 -#: netbox/templates/core/datasource.html:34 netbox/templates/core/job.html:44 -#: netbox/templates/core/plugin.html:54 netbox/templates/core/rq_worker.html:43 -#: netbox/templates/dcim/consoleport.html:28 -#: netbox/templates/dcim/consoleserverport.html:28 -#: netbox/templates/dcim/devicebay.html:24 -#: netbox/templates/dcim/devicerole.html:26 -#: netbox/templates/dcim/frontport.html:28 -#: netbox/templates/dcim/inc/interface_vlans_table.html:5 -#: netbox/templates/dcim/inc/panels/inventory_items.html:18 -#: netbox/templates/dcim/interface.html:38 -#: netbox/templates/dcim/interface.html:222 -#: netbox/templates/dcim/inventoryitem.html:28 -#: netbox/templates/dcim/inventoryitemrole.html:18 -#: netbox/templates/dcim/location.html:29 -#: netbox/templates/dcim/manufacturer.html:36 -#: netbox/templates/dcim/modulebay.html:30 -#: netbox/templates/dcim/platform.html:29 -#: netbox/templates/dcim/poweroutlet.html:28 -#: netbox/templates/dcim/powerport.html:28 -#: netbox/templates/dcim/rackrole.html:22 -#: netbox/templates/dcim/rearport.html:28 netbox/templates/dcim/region.html:29 -#: netbox/templates/dcim/sitegroup.html:29 -#: netbox/templates/dcim/virtualdevicecontext.html:18 -#: netbox/templates/extras/configcontext.html:13 -#: netbox/templates/extras/configtemplate.html:13 -#: netbox/templates/extras/customfield.html:13 -#: netbox/templates/extras/customlink.html:13 -#: netbox/templates/extras/eventrule.html:13 -#: netbox/templates/extras/exporttemplate.html:15 -#: netbox/templates/extras/notificationgroup.html:14 -#: netbox/templates/extras/savedfilter.html:13 -#: netbox/templates/extras/script_list.html:45 -#: netbox/templates/extras/tag.html:14 netbox/templates/extras/webhook.html:13 -#: netbox/templates/ipam/asnrange.html:15 -#: netbox/templates/ipam/fhrpgroup.html:30 netbox/templates/ipam/rir.html:22 -#: netbox/templates/ipam/role.html:22 netbox/templates/ipam/routetarget.html:13 -#: netbox/templates/ipam/service.html:24 -#: netbox/templates/ipam/servicetemplate.html:15 -#: netbox/templates/ipam/vlan.html:35 netbox/templates/ipam/vlangroup.html:30 -#: netbox/templates/ipam/vlantranslationpolicy.html:14 -#: netbox/templates/tenancy/contact.html:25 -#: netbox/templates/tenancy/contactgroup.html:21 -#: netbox/templates/tenancy/contactrole.html:18 -#: netbox/templates/tenancy/tenantgroup.html:29 -#: netbox/templates/users/group.html:17 -#: netbox/templates/users/objectpermission.html:17 -#: netbox/templates/virtualization/cluster.html:13 -#: netbox/templates/virtualization/clustergroup.html:22 -#: netbox/templates/virtualization/clustertype.html:22 -#: netbox/templates/virtualization/virtualdisk.html:25 -#: netbox/templates/virtualization/virtualmachine.html:15 -#: netbox/templates/virtualization/vminterface.html:25 -#: netbox/templates/vpn/ikepolicy.html:13 -#: netbox/templates/vpn/ikeproposal.html:13 -#: netbox/templates/vpn/ipsecpolicy.html:13 -#: netbox/templates/vpn/ipsecprofile.html:13 -#: netbox/templates/vpn/ipsecprofile.html:36 -#: netbox/templates/vpn/ipsecprofile.html:69 -#: netbox/templates/vpn/ipsecproposal.html:13 -#: netbox/templates/vpn/l2vpn.html:14 netbox/templates/vpn/tunnel.html:21 -#: netbox/templates/vpn/tunnelgroup.html:26 -#: netbox/templates/wireless/wirelesslangroup.html:29 -#: netbox/tenancy/tables/contacts.py:19 netbox/tenancy/tables/contacts.py:41 -#: netbox/tenancy/tables/contacts.py:56 netbox/tenancy/tables/tenants.py:16 -#: netbox/tenancy/tables/tenants.py:38 netbox/users/tables.py:62 -#: netbox/users/tables.py:76 netbox/virtualization/forms/bulk_create.py:20 -#: netbox/virtualization/forms/object_create.py:13 -#: netbox/virtualization/forms/object_create.py:23 -#: netbox/virtualization/tables/clusters.py:17 -#: netbox/virtualization/tables/clusters.py:39 -#: netbox/virtualization/tables/clusters.py:62 -#: netbox/virtualization/tables/virtualmachines.py:26 -#: netbox/virtualization/tables/virtualmachines.py:109 -#: netbox/virtualization/tables/virtualmachines.py:165 -#: netbox/vpn/tables/crypto.py:18 netbox/vpn/tables/crypto.py:57 -#: netbox/vpn/tables/crypto.py:93 netbox/vpn/tables/crypto.py:129 -#: netbox/vpn/tables/crypto.py:158 netbox/vpn/tables/l2vpn.py:23 -#: netbox/vpn/tables/tunnels.py:18 netbox/vpn/tables/tunnels.py:40 -#: netbox/wireless/tables/wirelesslan.py:18 -#: netbox/wireless/tables/wirelesslan.py:88 +#: circuits/tables/circuits.py:30 circuits/tables/circuits.py:167 +#: circuits/tables/providers.py:18 circuits/tables/providers.py:67 +#: circuits/tables/providers.py:97 circuits/tables/virtual_circuits.py:18 +#: core/tables/data.py:16 core/tables/jobs.py:14 core/tables/plugins.py:53 +#: core/tables/tasks.py:11 core/tables/tasks.py:115 dcim/forms/filtersets.py:65 +#: dcim/forms/object_create.py:43 dcim/tables/devices.py:63 +#: dcim/tables/devices.py:103 dcim/tables/devices.py:145 +#: dcim/tables/devices.py:299 dcim/tables/devices.py:402 +#: dcim/tables/devices.py:443 dcim/tables/devices.py:491 +#: dcim/tables/devices.py:545 dcim/tables/devices.py:568 +#: dcim/tables/devices.py:688 dcim/tables/devices.py:771 +#: dcim/tables/devices.py:817 dcim/tables/devices.py:879 +#: dcim/tables/devices.py:948 dcim/tables/devices.py:1013 +#: dcim/tables/devices.py:1032 dcim/tables/devices.py:1061 +#: dcim/tables/devices.py:1091 dcim/tables/devicetypes.py:31 +#: dcim/tables/devicetypes.py:227 dcim/tables/modules.py:17 +#: dcim/tables/power.py:22 dcim/tables/power.py:62 dcim/tables/racks.py:24 +#: dcim/tables/racks.py:117 dcim/tables/sites.py:24 dcim/tables/sites.py:54 +#: dcim/tables/sites.py:84 dcim/tables/sites.py:135 +#: extras/forms/filtersets.py:223 extras/tables/tables.py:62 +#: extras/tables/tables.py:126 extras/tables/tables.py:159 +#: extras/tables/tables.py:184 extras/tables/tables.py:260 +#: extras/tables/tables.py:290 extras/tables/tables.py:405 +#: extras/tables/tables.py:422 extras/tables/tables.py:445 +#: extras/tables/tables.py:483 extras/tables/tables.py:535 +#: extras/tables/tables.py:561 ipam/forms/bulk_edit.py:396 +#: ipam/forms/filtersets.py:410 ipam/forms/filtersets.py:496 +#: ipam/tables/asn.py:16 ipam/tables/ip.py:32 ipam/tables/ip.py:107 +#: ipam/tables/services.py:15 ipam/tables/services.py:40 +#: ipam/tables/vlans.py:33 ipam/tables/vlans.py:85 ipam/tables/vlans.py:233 +#: ipam/tables/vrfs.py:26 ipam/tables/vrfs.py:68 +#: templates/circuits/circuitgroup.html:28 +#: templates/circuits/circuittype.html:22 +#: templates/circuits/provideraccount.html:28 +#: templates/circuits/providernetwork.html:24 +#: templates/circuits/virtualcircuittype.html:22 +#: templates/core/datasource.html:34 templates/core/job.html:44 +#: templates/core/plugin.html:54 templates/core/rq_worker.html:43 +#: templates/dcim/consoleport.html:28 templates/dcim/consoleserverport.html:28 +#: templates/dcim/devicebay.html:24 templates/dcim/devicerole.html:26 +#: templates/dcim/frontport.html:28 +#: templates/dcim/inc/interface_vlans_table.html:5 +#: templates/dcim/inc/panels/inventory_items.html:18 +#: templates/dcim/interface.html:38 templates/dcim/interface.html:222 +#: templates/dcim/inventoryitem.html:28 +#: templates/dcim/inventoryitemrole.html:18 templates/dcim/location.html:29 +#: templates/dcim/manufacturer.html:36 templates/dcim/modulebay.html:30 +#: templates/dcim/moduletypeprofile.html:16 templates/dcim/platform.html:29 +#: templates/dcim/poweroutlet.html:28 templates/dcim/powerport.html:28 +#: templates/dcim/rackrole.html:22 templates/dcim/rearport.html:28 +#: templates/dcim/region.html:29 templates/dcim/sitegroup.html:29 +#: templates/dcim/virtualdevicecontext.html:18 +#: templates/extras/configcontext.html:13 +#: templates/extras/configtemplate.html:13 templates/extras/customfield.html:13 +#: templates/extras/customlink.html:13 templates/extras/eventrule.html:13 +#: templates/extras/exporttemplate.html:20 +#: templates/extras/notificationgroup.html:14 +#: templates/extras/savedfilter.html:13 templates/extras/script_list.html:45 +#: templates/extras/tableconfig.html:13 templates/extras/tag.html:14 +#: templates/extras/webhook.html:13 templates/ipam/asnrange.html:15 +#: templates/ipam/fhrpgroup.html:30 templates/ipam/rir.html:22 +#: templates/ipam/role.html:22 templates/ipam/routetarget.html:13 +#: templates/ipam/service.html:26 templates/ipam/servicetemplate.html:15 +#: templates/ipam/vlan.html:35 templates/ipam/vlangroup.html:30 +#: templates/ipam/vlantranslationpolicy.html:14 +#: templates/tenancy/contact.html:35 templates/tenancy/contactgroup.html:21 +#: templates/tenancy/contactrole.html:18 templates/tenancy/tenantgroup.html:29 +#: templates/users/group.html:17 templates/users/objectpermission.html:17 +#: templates/virtualization/cluster.html:13 +#: templates/virtualization/clustergroup.html:22 +#: templates/virtualization/clustertype.html:22 +#: templates/virtualization/virtualdisk.html:25 +#: templates/virtualization/virtualmachine.html:15 +#: templates/virtualization/vminterface.html:25 templates/vpn/ikepolicy.html:13 +#: templates/vpn/ikeproposal.html:13 templates/vpn/ipsecpolicy.html:13 +#: templates/vpn/ipsecprofile.html:13 templates/vpn/ipsecprofile.html:36 +#: templates/vpn/ipsecprofile.html:69 templates/vpn/ipsecproposal.html:13 +#: templates/vpn/l2vpn.html:14 templates/vpn/tunnel.html:21 +#: templates/vpn/tunnelgroup.html:26 +#: templates/wireless/wirelesslangroup.html:29 tenancy/tables/contacts.py:19 +#: tenancy/tables/contacts.py:45 tenancy/tables/contacts.py:60 +#: tenancy/tables/tenants.py:16 tenancy/tables/tenants.py:42 users/tables.py:62 +#: users/tables.py:76 virtualization/forms/bulk_create.py:20 +#: virtualization/forms/object_create.py:13 +#: virtualization/forms/object_create.py:23 +#: virtualization/tables/clusters.py:17 virtualization/tables/clusters.py:39 +#: virtualization/tables/clusters.py:62 +#: virtualization/tables/virtualmachines.py:26 +#: virtualization/tables/virtualmachines.py:109 +#: virtualization/tables/virtualmachines.py:165 vpn/tables/crypto.py:18 +#: vpn/tables/crypto.py:57 vpn/tables/crypto.py:93 vpn/tables/crypto.py:129 +#: vpn/tables/crypto.py:158 vpn/tables/l2vpn.py:23 vpn/tables/tunnels.py:18 +#: vpn/tables/tunnels.py:40 wireless/tables/wirelesslan.py:18 +#: wireless/tables/wirelesslan.py:88 msgid "Name" msgstr "" -#: netbox/circuits/tables/circuits.py:39 netbox/circuits/tables/circuits.py:173 -#: netbox/circuits/tables/providers.py:43 -#: netbox/circuits/tables/providers.py:77 -#: netbox/circuits/tables/virtual_circuits.py:27 -#: netbox/netbox/navigation/menu.py:274 netbox/netbox/navigation/menu.py:278 -#: netbox/netbox/navigation/menu.py:280 -#: netbox/templates/circuits/provider.html:57 -#: netbox/templates/circuits/provideraccount.html:44 -#: netbox/templates/circuits/providernetwork.html:50 +#: circuits/tables/circuits.py:39 circuits/tables/circuits.py:173 +#: circuits/tables/providers.py:43 circuits/tables/providers.py:77 +#: circuits/tables/virtual_circuits.py:27 netbox/navigation/menu.py:275 +#: netbox/navigation/menu.py:279 netbox/navigation/menu.py:281 +#: templates/circuits/provider.html:57 +#: templates/circuits/provideraccount.html:44 +#: templates/circuits/providernetwork.html:50 msgid "Circuits" msgstr "" -#: netbox/circuits/tables/circuits.py:54 -#: netbox/circuits/tables/virtual_circuits.py:42 -#: netbox/templates/circuits/circuit.html:26 -#: netbox/templates/circuits/virtualcircuit.html:35 -#: netbox/templates/dcim/interface.html:174 +#: circuits/tables/circuits.py:54 circuits/tables/virtual_circuits.py:42 +#: templates/circuits/circuit.html:26 templates/circuits/virtualcircuit.html:35 +#: templates/dcim/interface.html:174 msgid "Circuit ID" msgstr "" -#: netbox/circuits/tables/circuits.py:71 -#: netbox/wireless/forms/model_forms.py:163 +#: circuits/tables/circuits.py:71 wireless/forms/model_forms.py:164 msgid "Side A" msgstr "" -#: netbox/circuits/tables/circuits.py:76 +#: circuits/tables/circuits.py:76 msgid "Side Z" msgstr "" -#: netbox/circuits/tables/circuits.py:79 -#: netbox/templates/circuits/circuit.html:65 +#: circuits/tables/circuits.py:79 templates/circuits/circuit.html:65 msgid "Commit Rate" msgstr "" -#: netbox/circuits/tables/circuits.py:83 netbox/circuits/tables/providers.py:46 -#: netbox/circuits/tables/providers.py:80 -#: netbox/circuits/tables/providers.py:105 -#: netbox/circuits/tables/virtual_circuits.py:68 -#: netbox/dcim/tables/devices.py:1067 netbox/dcim/tables/devicetypes.py:97 -#: netbox/dcim/tables/modules.py:29 netbox/dcim/tables/modules.py:73 -#: netbox/dcim/tables/power.py:39 netbox/dcim/tables/power.py:96 -#: netbox/dcim/tables/racks.py:84 netbox/dcim/tables/racks.py:144 -#: netbox/dcim/tables/racks.py:224 netbox/dcim/tables/sites.py:107 -#: netbox/extras/tables/tables.py:588 netbox/ipam/tables/asn.py:69 -#: netbox/ipam/tables/fhrp.py:34 netbox/ipam/tables/ip.py:82 -#: netbox/ipam/tables/ip.py:226 netbox/ipam/tables/ip.py:281 -#: netbox/ipam/tables/ip.py:349 netbox/ipam/tables/services.py:24 -#: netbox/ipam/tables/services.py:54 netbox/ipam/tables/vlans.py:121 -#: netbox/ipam/tables/vrfs.py:47 netbox/ipam/tables/vrfs.py:72 -#: netbox/templates/dcim/htmx/cable_edit.html:92 -#: netbox/templates/generic/bulk_edit.html:86 -#: netbox/templates/inc/panels/comments.html:5 -#: netbox/tenancy/tables/contacts.py:68 netbox/tenancy/tables/tenants.py:46 -#: netbox/utilities/forms/fields/fields.py:29 -#: netbox/virtualization/tables/clusters.py:95 -#: netbox/virtualization/tables/virtualmachines.py:52 -#: netbox/vpn/tables/crypto.py:37 netbox/vpn/tables/crypto.py:74 -#: netbox/vpn/tables/crypto.py:109 netbox/vpn/tables/crypto.py:140 -#: netbox/vpn/tables/crypto.py:173 netbox/vpn/tables/l2vpn.py:37 -#: netbox/vpn/tables/tunnels.py:61 netbox/wireless/tables/wirelesslan.py:27 -#: netbox/wireless/tables/wirelesslan.py:66 +#: circuits/tables/circuits.py:83 circuits/tables/providers.py:46 +#: circuits/tables/providers.py:80 circuits/tables/providers.py:105 +#: circuits/tables/virtual_circuits.py:68 dcim/tables/devices.py:1074 +#: dcim/tables/devicetypes.py:97 dcim/tables/modules.py:27 +#: dcim/tables/modules.py:68 dcim/tables/modules.py:107 dcim/tables/power.py:39 +#: dcim/tables/power.py:96 dcim/tables/racks.py:88 dcim/tables/racks.py:148 +#: dcim/tables/racks.py:233 dcim/tables/sites.py:36 dcim/tables/sites.py:66 +#: dcim/tables/sites.py:113 dcim/tables/sites.py:167 +#: extras/tables/tables.py:643 ipam/tables/asn.py:69 ipam/tables/fhrp.py:34 +#: ipam/tables/ip.py:83 ipam/tables/ip.py:227 ipam/tables/ip.py:286 +#: ipam/tables/ip.py:355 ipam/tables/services.py:24 ipam/tables/services.py:54 +#: ipam/tables/vlans.py:123 ipam/tables/vrfs.py:47 ipam/tables/vrfs.py:72 +#: templates/dcim/htmx/cable_edit.html:92 templates/generic/bulk_edit.html:86 +#: templates/inc/panels/comments.html:5 tenancy/tables/contacts.py:31 +#: tenancy/tables/contacts.py:72 tenancy/tables/tenants.py:28 +#: tenancy/tables/tenants.py:50 utilities/forms/fields/fields.py:29 +#: virtualization/tables/clusters.py:95 +#: virtualization/tables/virtualmachines.py:52 vpn/tables/crypto.py:37 +#: vpn/tables/crypto.py:74 vpn/tables/crypto.py:109 vpn/tables/crypto.py:140 +#: vpn/tables/crypto.py:173 vpn/tables/l2vpn.py:40 vpn/tables/tunnels.py:61 +#: wireless/tables/wirelesslan.py:27 wireless/tables/wirelesslan.py:66 msgid "Comments" msgstr "" -#: netbox/circuits/tables/circuits.py:89 -#: netbox/templates/tenancy/contact.html:84 -#: netbox/tenancy/tables/contacts.py:73 +#: circuits/tables/circuits.py:89 templates/tenancy/contact.html:94 +#: tenancy/tables/contacts.py:77 msgid "Assignments" msgstr "" -#: netbox/circuits/tables/circuits.py:116 netbox/dcim/forms/connections.py:81 +#: circuits/tables/circuits.py:116 dcim/forms/connections.py:81 msgid "Side" msgstr "" -#: netbox/circuits/tables/circuits.py:119 +#: circuits/tables/circuits.py:119 msgid "Termination Type" msgstr "" -#: netbox/circuits/tables/circuits.py:122 +#: circuits/tables/circuits.py:122 msgid "Termination Point" msgstr "" -#: netbox/circuits/tables/circuits.py:133 netbox/dcim/tables/devices.py:160 -#: netbox/templates/dcim/sitegroup.html:26 +#: circuits/tables/circuits.py:133 dcim/tables/devices.py:160 +#: templates/dcim/sitegroup.html:26 msgid "Site Group" msgstr "" -#: netbox/circuits/tables/circuits.py:148 -#: netbox/templates/circuits/providernetwork.html:17 -#: netbox/templates/circuits/virtualcircuit.html:27 -#: netbox/templates/circuits/virtualcircuittermination.html:30 -#: netbox/templates/dcim/interface.html:170 +#: circuits/tables/circuits.py:148 templates/circuits/providernetwork.html:17 +#: templates/circuits/virtualcircuit.html:27 +#: templates/circuits/virtualcircuittermination.html:30 +#: templates/dcim/interface.html:170 msgid "Provider Network" msgstr "" -#: netbox/circuits/tables/providers.py:23 +#: circuits/tables/providers.py:23 msgid "Accounts" msgstr "" -#: netbox/circuits/tables/providers.py:28 +#: circuits/tables/providers.py:28 msgid "Account Count" msgstr "" -#: netbox/circuits/tables/providers.py:37 netbox/dcim/tables/sites.py:99 +#: circuits/tables/providers.py:37 dcim/tables/sites.py:105 msgid "ASN Count" msgstr "" -#: netbox/circuits/tables/virtual_circuits.py:65 -#: netbox/netbox/navigation/menu.py:234 -#: netbox/templates/circuits/virtualcircuit.html:87 -#: netbox/templates/vpn/l2vpn.html:56 netbox/templates/vpn/tunnel.html:72 -#: netbox/vpn/tables/tunnels.py:58 +#: circuits/tables/virtual_circuits.py:65 netbox/navigation/menu.py:235 +#: templates/circuits/virtualcircuit.html:87 templates/vpn/l2vpn.html:60 +#: templates/vpn/tunnel.html:72 vpn/tables/tunnels.py:58 msgid "Terminations" msgstr "" -#: netbox/circuits/tables/virtual_circuits.py:109 -#: netbox/dcim/forms/bulk_edit.py:745 netbox/dcim/forms/bulk_edit.py:1299 -#: netbox/dcim/forms/bulk_edit.py:1708 netbox/dcim/forms/bulk_edit.py:1760 -#: netbox/dcim/forms/bulk_import.py:668 netbox/dcim/forms/bulk_import.py:730 -#: netbox/dcim/forms/bulk_import.py:756 netbox/dcim/forms/bulk_import.py:782 -#: netbox/dcim/forms/bulk_import.py:802 netbox/dcim/forms/bulk_import.py:858 -#: netbox/dcim/forms/bulk_import.py:976 netbox/dcim/forms/bulk_import.py:1024 -#: netbox/dcim/forms/bulk_import.py:1041 netbox/dcim/forms/bulk_import.py:1053 -#: netbox/dcim/forms/bulk_import.py:1101 netbox/dcim/forms/bulk_import.py:1223 -#: netbox/dcim/forms/bulk_import.py:1559 netbox/dcim/forms/connections.py:24 -#: netbox/dcim/forms/filtersets.py:132 netbox/dcim/forms/filtersets.py:922 -#: netbox/dcim/forms/filtersets.py:954 netbox/dcim/forms/filtersets.py:1100 -#: netbox/dcim/forms/filtersets.py:1291 netbox/dcim/forms/filtersets.py:1316 -#: netbox/dcim/forms/filtersets.py:1340 netbox/dcim/forms/filtersets.py:1360 -#: netbox/dcim/forms/filtersets.py:1388 netbox/dcim/forms/filtersets.py:1508 -#: netbox/dcim/forms/filtersets.py:1533 netbox/dcim/forms/filtersets.py:1557 -#: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 -#: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 -#: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 -#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 -#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 -#: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 -#: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 -#: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 -#: netbox/dcim/tables/devices.py:513 netbox/dcim/tables/devices.py:618 -#: netbox/dcim/tables/devices.py:730 netbox/dcim/tables/devices.py:786 -#: netbox/dcim/tables/devices.py:832 netbox/dcim/tables/devices.py:891 -#: netbox/dcim/tables/devices.py:959 netbox/dcim/tables/devices.py:1088 -#: netbox/dcim/tables/modules.py:53 netbox/extras/forms/filtersets.py:328 -#: netbox/ipam/forms/bulk_import.py:310 netbox/ipam/forms/bulk_import.py:556 -#: netbox/ipam/forms/filtersets.py:618 netbox/ipam/forms/model_forms.py:333 -#: netbox/ipam/forms/model_forms.py:761 netbox/ipam/forms/model_forms.py:794 -#: netbox/ipam/forms/model_forms.py:820 netbox/ipam/tables/vlans.py:156 -#: netbox/templates/circuits/virtualcircuittermination.html:56 -#: netbox/templates/dcim/consoleport.html:20 -#: netbox/templates/dcim/consoleserverport.html:20 -#: netbox/templates/dcim/device.html:15 netbox/templates/dcim/device.html:130 -#: netbox/templates/dcim/device_edit.html:12 -#: netbox/templates/dcim/devicebay.html:20 -#: netbox/templates/dcim/devicebay.html:48 -#: netbox/templates/dcim/frontport.html:20 -#: netbox/templates/dcim/interface.html:30 -#: netbox/templates/dcim/interface.html:218 -#: netbox/templates/dcim/inventoryitem.html:20 -#: netbox/templates/dcim/module.html:57 netbox/templates/dcim/modulebay.html:20 -#: netbox/templates/dcim/poweroutlet.html:20 -#: netbox/templates/dcim/powerport.html:20 -#: netbox/templates/dcim/rearport.html:20 -#: netbox/templates/dcim/virtualchassis.html:65 -#: netbox/templates/dcim/virtualchassis_edit.html:55 -#: netbox/templates/dcim/virtualdevicecontext.html:22 -#: netbox/templates/virtualization/virtualmachine.html:114 -#: netbox/templates/vpn/tunneltermination.html:23 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:6 -#: netbox/virtualization/filtersets.py:133 -#: netbox/virtualization/forms/bulk_edit.py:119 -#: netbox/virtualization/forms/bulk_import.py:105 -#: netbox/virtualization/forms/filtersets.py:134 -#: netbox/virtualization/forms/model_forms.py:192 -#: netbox/virtualization/tables/virtualmachines.py:41 netbox/vpn/choices.py:52 -#: netbox/vpn/forms/bulk_import.py:86 netbox/vpn/forms/bulk_import.py:283 -#: netbox/vpn/forms/filtersets.py:281 netbox/vpn/forms/model_forms.py:91 -#: netbox/vpn/forms/model_forms.py:126 netbox/vpn/forms/model_forms.py:237 -#: netbox/vpn/forms/model_forms.py:456 netbox/wireless/forms/model_forms.py:102 -#: netbox/wireless/forms/model_forms.py:144 -#: netbox/wireless/tables/wirelesslan.py:84 +#: circuits/tables/virtual_circuits.py:109 dcim/forms/bulk_edit.py:789 +#: dcim/forms/bulk_edit.py:1343 dcim/forms/bulk_edit.py:1755 +#: dcim/forms/bulk_edit.py:1807 dcim/forms/bulk_import.py:699 +#: dcim/forms/bulk_import.py:761 dcim/forms/bulk_import.py:787 +#: dcim/forms/bulk_import.py:813 dcim/forms/bulk_import.py:833 +#: dcim/forms/bulk_import.py:889 dcim/forms/bulk_import.py:1007 +#: dcim/forms/bulk_import.py:1055 dcim/forms/bulk_import.py:1072 +#: dcim/forms/bulk_import.py:1084 dcim/forms/bulk_import.py:1132 +#: dcim/forms/bulk_import.py:1254 dcim/forms/bulk_import.py:1590 +#: dcim/forms/connections.py:24 dcim/forms/filtersets.py:133 +#: dcim/forms/filtersets.py:941 dcim/forms/filtersets.py:973 +#: dcim/forms/filtersets.py:1119 dcim/forms/filtersets.py:1310 +#: dcim/forms/filtersets.py:1335 dcim/forms/filtersets.py:1359 +#: dcim/forms/filtersets.py:1379 dcim/forms/filtersets.py:1412 +#: dcim/forms/filtersets.py:1532 dcim/forms/filtersets.py:1557 +#: dcim/forms/filtersets.py:1581 dcim/forms/filtersets.py:1599 +#: dcim/forms/filtersets.py:1616 dcim/forms/filtersets.py:1713 +#: dcim/forms/filtersets.py:1737 dcim/forms/filtersets.py:1761 +#: dcim/forms/model_forms.py:738 dcim/forms/model_forms.py:955 +#: dcim/forms/model_forms.py:1356 dcim/forms/model_forms.py:1841 +#: dcim/forms/model_forms.py:1912 dcim/forms/object_create.py:260 +#: dcim/tables/connections.py:22 dcim/tables/connections.py:41 +#: dcim/tables/connections.py:60 dcim/tables/devices.py:295 +#: dcim/tables/devices.py:380 dcim/tables/devices.py:421 +#: dcim/tables/devices.py:463 dcim/tables/devices.py:513 +#: dcim/tables/devices.py:625 dcim/tables/devices.py:737 +#: dcim/tables/devices.py:793 dcim/tables/devices.py:839 +#: dcim/tables/devices.py:898 dcim/tables/devices.py:966 +#: dcim/tables/devices.py:1095 dcim/tables/modules.py:87 +#: extras/forms/filtersets.py:363 ipam/forms/bulk_import.py:310 +#: ipam/forms/filtersets.py:626 ipam/forms/model_forms.py:333 +#: ipam/tables/vlans.py:158 +#: templates/circuits/virtualcircuittermination.html:56 +#: templates/dcim/consoleport.html:20 templates/dcim/consoleserverport.html:20 +#: templates/dcim/device.html:15 templates/dcim/device.html:130 +#: templates/dcim/device_edit.html:12 templates/dcim/devicebay.html:20 +#: templates/dcim/devicebay.html:48 templates/dcim/frontport.html:20 +#: templates/dcim/interface.html:30 templates/dcim/interface.html:218 +#: templates/dcim/inventoryitem.html:20 templates/dcim/module.html:57 +#: templates/dcim/modulebay.html:20 templates/dcim/poweroutlet.html:20 +#: templates/dcim/powerport.html:20 templates/dcim/rearport.html:20 +#: templates/dcim/virtualchassis.html:65 +#: templates/dcim/virtualchassis_edit.html:55 +#: templates/dcim/virtualdevicecontext.html:22 +#: templates/virtualization/virtualmachine.html:114 +#: templates/vpn/tunneltermination.html:23 +#: templates/wireless/inc/wirelesslink_interface.html:6 +#: virtualization/filtersets.py:133 virtualization/forms/bulk_edit.py:119 +#: virtualization/forms/bulk_import.py:105 +#: virtualization/forms/filtersets.py:134 +#: virtualization/forms/model_forms.py:192 +#: virtualization/tables/virtualmachines.py:41 vpn/choices.py:52 +#: vpn/forms/bulk_import.py:86 vpn/forms/bulk_import.py:288 +#: vpn/forms/filtersets.py:286 vpn/forms/model_forms.py:91 +#: vpn/forms/model_forms.py:126 vpn/forms/model_forms.py:237 +#: vpn/forms/model_forms.py:456 wireless/forms/model_forms.py:103 +#: wireless/forms/model_forms.py:145 wireless/tables/wirelesslan.py:84 msgid "Device" msgstr "" -#: netbox/circuits/views.py:373 +#: circuits/views.py:362 #, python-brace-format msgid "No terminations have been defined for circuit {circuit}." msgstr "" -#: netbox/circuits/views.py:422 +#: circuits/views.py:411 #, python-brace-format msgid "Swapped terminations for circuit {circuit}." msgstr "" -#: netbox/core/api/views.py:51 +#: core/api/views.py:51 msgid "This user does not have permission to synchronize this data source." msgstr "" -#: netbox/core/apps.py:33 +#: core/apps.py:34 msgid "Object created" msgstr "" -#: netbox/core/apps.py:34 +#: core/apps.py:35 msgid "Object updated" msgstr "" -#: netbox/core/apps.py:35 +#: core/apps.py:36 msgid "Object deleted" msgstr "" -#: netbox/core/apps.py:36 +#: core/apps.py:37 msgid "Job started" msgstr "" -#: netbox/core/apps.py:37 +#: core/apps.py:38 msgid "Job completed" msgstr "" -#: netbox/core/apps.py:38 +#: core/apps.py:39 msgid "Job failed" msgstr "" -#: netbox/core/apps.py:39 +#: core/apps.py:40 msgid "Job errored" msgstr "" -#: netbox/core/choices.py:18 +#: core/choices.py:18 msgid "New" msgstr "" -#: netbox/core/choices.py:19 netbox/core/constants.py:18 -#: netbox/core/tables/tasks.py:15 netbox/templates/core/rq_task.html:77 +#: core/choices.py:19 core/constants.py:18 core/tables/tasks.py:15 +#: templates/core/rq_task.html:77 msgid "Queued" msgstr "" -#: netbox/core/choices.py:20 +#: core/choices.py:20 msgid "Syncing" msgstr "" -#: netbox/core/choices.py:21 netbox/core/choices.py:57 -#: netbox/core/tables/jobs.py:41 netbox/templates/core/job.html:86 +#: core/choices.py:21 core/choices.py:57 core/tables/jobs.py:41 +#: templates/core/job.html:86 msgid "Completed" msgstr "" -#: netbox/core/choices.py:22 netbox/core/choices.py:59 -#: netbox/core/constants.py:20 netbox/core/tables/tasks.py:34 -#: netbox/dcim/choices.py:187 netbox/dcim/choices.py:239 -#: netbox/dcim/choices.py:1601 netbox/dcim/choices.py:1674 -#: netbox/virtualization/choices.py:48 +#: core/choices.py:22 core/choices.py:59 core/constants.py:20 +#: core/tables/tasks.py:34 dcim/choices.py:188 dcim/choices.py:241 +#: dcim/choices.py:1604 dcim/choices.py:1694 virtualization/choices.py:48 msgid "Failed" msgstr "" -#: netbox/core/choices.py:35 netbox/netbox/navigation/menu.py:356 -#: netbox/netbox/navigation/menu.py:360 -#: netbox/templates/extras/script/base.html:14 -#: netbox/templates/extras/script_list.html:7 -#: netbox/templates/extras/script_list.html:12 -#: netbox/templates/extras/script_result.html:17 +#: core/choices.py:35 netbox/navigation/menu.py:358 +#: netbox/navigation/menu.py:362 templates/extras/script/base.html:14 +#: templates/extras/script_list.html:7 templates/extras/script_list.html:12 +#: templates/extras/script_result.html:17 msgid "Scripts" msgstr "" -#: netbox/core/choices.py:36 netbox/templates/extras/report/base.html:13 +#: core/choices.py:36 templates/extras/report/base.html:13 msgid "Reports" msgstr "" -#: netbox/core/choices.py:54 +#: core/choices.py:54 msgid "Pending" msgstr "" -#: netbox/core/choices.py:55 netbox/core/constants.py:23 -#: netbox/core/tables/jobs.py:32 netbox/core/tables/tasks.py:38 -#: netbox/templates/core/job.html:73 +#: core/choices.py:55 core/constants.py:23 core/tables/jobs.py:32 +#: core/tables/tasks.py:38 templates/core/job.html:73 msgid "Scheduled" msgstr "" -#: netbox/core/choices.py:56 +#: core/choices.py:56 msgid "Running" msgstr "" -#: netbox/core/choices.py:58 +#: core/choices.py:58 msgid "Errored" msgstr "" -#: netbox/core/choices.py:82 +#: core/choices.py:82 msgid "Minutely" msgstr "" -#: netbox/core/choices.py:83 +#: core/choices.py:83 msgid "Hourly" msgstr "" -#: netbox/core/choices.py:84 +#: core/choices.py:84 msgid "12 hours" msgstr "" -#: netbox/core/choices.py:85 +#: core/choices.py:85 msgid "Daily" msgstr "" -#: netbox/core/choices.py:86 +#: core/choices.py:86 msgid "Weekly" msgstr "" -#: netbox/core/choices.py:87 +#: core/choices.py:87 msgid "30 days" msgstr "" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 -#: netbox/templates/generic/object.html:61 +#: core/choices.py:103 core/tables/plugins.py:75 +#: templates/generic/object.html:61 msgid "Updated" msgstr "" -#: netbox/core/choices.py:104 +#: core/choices.py:104 msgid "Deleted" msgstr "" -#: netbox/core/constants.py:19 netbox/core/tables/tasks.py:30 +#: core/constants.py:19 core/tables/tasks.py:30 msgid "Finished" msgstr "" -#: netbox/core/constants.py:21 netbox/core/tables/jobs.py:38 -#: netbox/templates/core/job.html:82 -#: netbox/templates/extras/htmx/script_result.html:8 +#: core/constants.py:21 core/tables/jobs.py:38 templates/core/job.html:82 +#: templates/extras/htmx/script_result.html:8 msgid "Started" msgstr "" -#: netbox/core/constants.py:22 netbox/core/tables/tasks.py:26 +#: core/constants.py:22 core/tables/tasks.py:26 msgid "Deferred" msgstr "" -#: netbox/core/constants.py:24 +#: core/constants.py:24 msgid "Stopped" msgstr "" -#: netbox/core/constants.py:25 +#: core/constants.py:25 msgid "Cancelled" msgstr "" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 -#: netbox/templates/core/plugin.html:88 -#: netbox/templates/dcim/interface.html:273 +#: core/data_backends.py:32 core/tables/plugins.py:61 +#: templates/core/plugin.html:88 templates/dcim/interface.html:273 msgid "Local" msgstr "" -#: netbox/core/data_backends.py:50 netbox/core/tables/change_logging.py:20 -#: netbox/templates/account/profile.html:15 netbox/templates/users/user.html:17 -#: netbox/users/tables.py:31 +#: core/data_backends.py:50 core/tables/change_logging.py:20 +#: templates/account/profile.html:15 templates/users/user.html:17 +#: users/tables.py:31 msgid "Username" msgstr "" -#: netbox/core/data_backends.py:52 netbox/core/data_backends.py:58 +#: core/data_backends.py:52 core/data_backends.py:58 msgid "Only used for cloning with HTTP(S)" msgstr "" -#: netbox/core/data_backends.py:56 netbox/templates/account/base.html:23 -#: netbox/templates/account/password.html:12 -#: netbox/users/forms/model_forms.py:170 +#: core/data_backends.py:56 templates/account/base.html:23 +#: templates/account/password.html:12 users/forms/model_forms.py:170 msgid "Password" msgstr "" -#: netbox/core/data_backends.py:62 +#: core/data_backends.py:62 msgid "Branch" msgstr "" -#: netbox/core/data_backends.py:120 +#: core/data_backends.py:120 #, python-brace-format msgid "Fetching remote data failed ({name}): {error}" msgstr "" -#: netbox/core/data_backends.py:133 +#: core/data_backends.py:133 msgid "AWS access key ID" msgstr "" -#: netbox/core/data_backends.py:137 +#: core/data_backends.py:137 msgid "AWS secret access key" msgstr "" -#: netbox/core/filtersets.py:53 netbox/extras/filtersets.py:250 -#: netbox/extras/filtersets.py:633 netbox/extras/filtersets.py:661 +#: core/filtersets.py:57 extras/filtersets.py:254 extras/filtersets.py:726 +#: extras/filtersets.py:754 msgid "Data source (ID)" msgstr "" -#: netbox/core/filtersets.py:59 +#: core/filtersets.py:63 msgid "Data source (name)" msgstr "" -#: netbox/core/filtersets.py:145 netbox/dcim/filtersets.py:502 -#: netbox/extras/filtersets.py:287 netbox/extras/filtersets.py:331 -#: netbox/extras/filtersets.py:353 netbox/extras/filtersets.py:413 -#: netbox/users/filtersets.py:28 +#: core/filtersets.py:149 dcim/filtersets.py:504 extras/filtersets.py:292 +#: extras/filtersets.py:344 extras/filtersets.py:389 extras/filtersets.py:411 +#: extras/filtersets.py:471 users/filtersets.py:28 msgid "User (ID)" msgstr "" -#: netbox/core/filtersets.py:151 +#: core/filtersets.py:155 msgid "User name" msgstr "" -#: netbox/core/forms/bulk_edit.py:25 netbox/core/forms/filtersets.py:43 -#: netbox/core/tables/data.py:26 netbox/dcim/forms/bulk_edit.py:1140 -#: netbox/dcim/forms/bulk_edit.py:1418 netbox/dcim/forms/filtersets.py:1424 -#: netbox/dcim/tables/devices.py:566 netbox/dcim/tables/devicetypes.py:231 -#: netbox/extras/forms/bulk_edit.py:123 netbox/extras/forms/bulk_edit.py:187 -#: netbox/extras/forms/bulk_edit.py:246 netbox/extras/forms/filtersets.py:145 -#: netbox/extras/forms/filtersets.py:235 netbox/extras/forms/filtersets.py:300 -#: netbox/extras/tables/tables.py:165 netbox/extras/tables/tables.py:256 -#: netbox/extras/tables/tables.py:418 netbox/netbox/preferences.py:22 -#: netbox/templates/core/datasource.html:42 -#: netbox/templates/dcim/interface.html:61 -#: netbox/templates/extras/customlink.html:17 -#: netbox/templates/extras/eventrule.html:17 -#: netbox/templates/extras/savedfilter.html:25 -#: netbox/templates/users/objectpermission.html:25 -#: netbox/templates/virtualization/vminterface.html:29 -#: netbox/users/forms/bulk_edit.py:89 netbox/users/forms/filtersets.py:70 -#: netbox/users/tables.py:83 netbox/virtualization/forms/bulk_edit.py:199 -#: netbox/virtualization/forms/filtersets.py:223 +#: core/forms/bulk_edit.py:26 core/forms/filtersets.py:43 +#: core/tables/data.py:26 dcim/choices.py:1652 dcim/forms/bulk_edit.py:1184 +#: dcim/forms/bulk_edit.py:1465 dcim/forms/filtersets.py:1448 +#: dcim/tables/devices.py:573 dcim/tables/devicetypes.py:231 +#: extras/forms/bulk_edit.py:124 extras/forms/bulk_edit.py:192 +#: extras/forms/bulk_edit.py:220 extras/forms/bulk_edit.py:279 +#: extras/forms/filtersets.py:146 extras/forms/filtersets.py:240 +#: extras/forms/filtersets.py:270 extras/forms/filtersets.py:335 +#: extras/tables/tables.py:166 extras/tables/tables.py:267 +#: extras/tables/tables.py:300 extras/tables/tables.py:459 +#: netbox/preferences.py:22 templates/core/datasource.html:42 +#: templates/dcim/interface.html:61 templates/extras/customlink.html:17 +#: templates/extras/eventrule.html:17 templates/extras/savedfilter.html:25 +#: templates/extras/tableconfig.html:33 +#: templates/users/objectpermission.html:25 +#: templates/virtualization/vminterface.html:29 users/forms/bulk_edit.py:89 +#: users/forms/filtersets.py:70 users/tables.py:83 +#: virtualization/forms/bulk_edit.py:199 virtualization/forms/filtersets.py:223 msgid "Enabled" msgstr "" -#: netbox/core/forms/bulk_edit.py:34 netbox/extras/forms/model_forms.py:299 -#: netbox/templates/extras/savedfilter.html:52 -#: netbox/vpn/forms/filtersets.py:102 netbox/vpn/forms/filtersets.py:132 -#: netbox/vpn/forms/filtersets.py:156 netbox/vpn/forms/filtersets.py:175 -#: netbox/vpn/forms/model_forms.py:302 netbox/vpn/forms/model_forms.py:323 -#: netbox/vpn/forms/model_forms.py:339 netbox/vpn/forms/model_forms.py:360 -#: netbox/vpn/forms/model_forms.py:383 +#: core/forms/bulk_edit.py:36 core/forms/filtersets.py:50 +#: core/tables/data.py:29 templates/core/datasource.html:50 +msgid "Sync interval" +msgstr "" + +#: core/forms/bulk_edit.py:40 extras/forms/model_forms.py:304 +#: templates/extras/savedfilter.html:52 vpn/forms/filtersets.py:102 +#: vpn/forms/filtersets.py:132 vpn/forms/filtersets.py:156 +#: vpn/forms/filtersets.py:175 vpn/forms/model_forms.py:302 +#: vpn/forms/model_forms.py:323 vpn/forms/model_forms.py:339 +#: vpn/forms/model_forms.py:360 vpn/forms/model_forms.py:383 msgid "Parameters" msgstr "" -#: netbox/core/forms/bulk_edit.py:38 netbox/templates/core/datasource.html:68 +#: core/forms/bulk_edit.py:44 templates/core/datasource.html:72 msgid "Ignore rules" msgstr "" -#: netbox/core/forms/filtersets.py:30 netbox/core/forms/model_forms.py:97 -#: netbox/extras/forms/model_forms.py:262 -#: netbox/extras/forms/model_forms.py:592 -#: netbox/extras/forms/model_forms.py:646 netbox/extras/tables/tables.py:194 -#: netbox/extras/tables/tables.py:486 netbox/extras/tables/tables.py:524 -#: netbox/templates/core/datasource.html:31 -#: netbox/templates/extras/configcontext.html:29 -#: netbox/templates/extras/configtemplate.html:21 -#: netbox/templates/extras/exporttemplate.html:35 -#: netbox/templates/extras/object_render_config.html:19 +#: core/forms/filtersets.py:30 core/forms/model_forms.py:100 +#: extras/forms/model_forms.py:265 extras/forms/model_forms.py:660 +#: extras/forms/model_forms.py:713 extras/tables/tables.py:204 +#: extras/tables/tables.py:527 extras/tables/tables.py:565 +#: templates/core/datasource.html:31 templates/extras/configcontext.html:29 +#: templates/extras/configtemplate.html:37 +#: templates/extras/exporttemplate.html:44 +#: templates/extras/object_render_config.html:19 msgid "Data Source" msgstr "" -#: netbox/core/forms/filtersets.py:55 netbox/core/forms/mixins.py:21 +#: core/forms/filtersets.py:60 core/forms/mixins.py:21 msgid "File" msgstr "" -#: netbox/core/forms/filtersets.py:60 netbox/core/forms/mixins.py:16 -#: netbox/extras/forms/filtersets.py:174 netbox/extras/forms/filtersets.py:335 -#: netbox/extras/forms/filtersets.py:421 +#: core/forms/filtersets.py:65 core/forms/mixins.py:16 +#: extras/forms/filtersets.py:175 extras/forms/filtersets.py:370 +#: extras/forms/filtersets.py:457 msgid "Data source" msgstr "" -#: netbox/core/forms/filtersets.py:71 netbox/extras/forms/filtersets.py:448 +#: core/forms/filtersets.py:76 extras/forms/filtersets.py:503 msgid "Creation" msgstr "" -#: netbox/core/forms/filtersets.py:75 netbox/core/forms/filtersets.py:161 -#: netbox/extras/forms/filtersets.py:469 netbox/extras/tables/tables.py:223 -#: netbox/extras/tables/tables.py:297 netbox/extras/tables/tables.py:329 -#: netbox/extras/tables/tables.py:577 netbox/templates/core/job.html:38 -#: netbox/templates/core/objectchange.html:52 -#: netbox/tenancy/tables/contacts.py:90 netbox/vpn/tables/l2vpn.py:59 +#: core/forms/filtersets.py:80 core/forms/filtersets.py:166 +#: extras/forms/filtersets.py:524 extras/tables/tables.py:234 +#: extras/tables/tables.py:294 extras/tables/tables.py:338 +#: extras/tables/tables.py:370 extras/tables/tables.py:632 +#: templates/core/job.html:38 templates/core/objectchange.html:52 +#: templates/extras/tableconfig.html:21 tenancy/tables/contacts.py:94 +#: vpn/tables/l2vpn.py:62 msgid "Object Type" msgstr "" -#: netbox/core/forms/filtersets.py:85 +#: core/forms/filtersets.py:90 msgid "Created after" msgstr "" -#: netbox/core/forms/filtersets.py:90 +#: core/forms/filtersets.py:95 msgid "Created before" msgstr "" -#: netbox/core/forms/filtersets.py:95 +#: core/forms/filtersets.py:100 msgid "Scheduled after" msgstr "" -#: netbox/core/forms/filtersets.py:100 +#: core/forms/filtersets.py:105 msgid "Scheduled before" msgstr "" -#: netbox/core/forms/filtersets.py:105 +#: core/forms/filtersets.py:110 msgid "Started after" msgstr "" -#: netbox/core/forms/filtersets.py:110 +#: core/forms/filtersets.py:115 msgid "Started before" msgstr "" -#: netbox/core/forms/filtersets.py:115 +#: core/forms/filtersets.py:120 msgid "Completed after" msgstr "" -#: netbox/core/forms/filtersets.py:120 +#: core/forms/filtersets.py:125 msgid "Completed before" msgstr "" -#: netbox/core/forms/filtersets.py:127 netbox/core/forms/filtersets.py:156 -#: netbox/dcim/forms/bulk_edit.py:465 netbox/dcim/forms/filtersets.py:419 -#: netbox/dcim/forms/filtersets.py:463 netbox/dcim/forms/model_forms.py:324 -#: netbox/extras/forms/filtersets.py:464 netbox/extras/forms/filtersets.py:484 -#: netbox/extras/tables/tables.py:305 netbox/extras/tables/tables.py:345 -#: netbox/templates/core/objectchange.html:36 -#: netbox/templates/dcim/rackreservation.html:58 -#: netbox/templates/extras/savedfilter.html:21 -#: netbox/templates/inc/user_menu.html:33 netbox/templates/users/token.html:21 -#: netbox/templates/users/user.html:6 netbox/templates/users/user.html:14 -#: netbox/users/filtersets.py:107 netbox/users/filtersets.py:174 -#: netbox/users/forms/filtersets.py:84 netbox/users/forms/filtersets.py:125 -#: netbox/users/forms/model_forms.py:155 netbox/users/forms/model_forms.py:192 -#: netbox/users/tables.py:19 +#: core/forms/filtersets.py:132 core/forms/filtersets.py:161 +#: dcim/forms/bulk_edit.py:479 dcim/forms/filtersets.py:420 +#: dcim/forms/filtersets.py:464 dcim/forms/model_forms.py:332 +#: extras/forms/filtersets.py:519 extras/forms/filtersets.py:539 +#: extras/tables/tables.py:346 extras/tables/tables.py:386 +#: templates/core/objectchange.html:36 templates/dcim/rackreservation.html:58 +#: templates/extras/savedfilter.html:21 templates/extras/tableconfig.html:29 +#: templates/inc/user_menu.html:33 templates/users/token.html:21 +#: templates/users/user.html:6 templates/users/user.html:14 +#: users/filtersets.py:107 users/filtersets.py:174 users/forms/filtersets.py:84 +#: users/forms/filtersets.py:125 users/forms/model_forms.py:155 +#: users/forms/model_forms.py:192 users/tables.py:19 msgid "User" msgstr "" -#: netbox/core/forms/filtersets.py:135 netbox/core/tables/change_logging.py:15 -#: netbox/extras/tables/tables.py:615 netbox/extras/tables/tables.py:669 -#: netbox/templates/core/objectchange.html:32 +#: core/forms/filtersets.py:140 core/tables/change_logging.py:15 +#: extras/tables/tables.py:670 extras/tables/tables.py:724 +#: templates/core/objectchange.html:32 msgid "Time" msgstr "" -#: netbox/core/forms/filtersets.py:140 netbox/extras/forms/filtersets.py:453 +#: core/forms/filtersets.py:145 extras/forms/filtersets.py:508 msgid "After" msgstr "" -#: netbox/core/forms/filtersets.py:145 netbox/extras/forms/filtersets.py:458 +#: core/forms/filtersets.py:150 extras/forms/filtersets.py:513 msgid "Before" msgstr "" -#: netbox/core/forms/filtersets.py:149 netbox/core/tables/change_logging.py:29 -#: netbox/extras/forms/model_forms.py:410 -#: netbox/templates/core/objectchange.html:46 -#: netbox/templates/extras/eventrule.html:71 +#: core/forms/filtersets.py:154 core/tables/change_logging.py:29 +#: extras/forms/model_forms.py:474 templates/core/objectchange.html:46 +#: templates/extras/eventrule.html:71 msgid "Action" msgstr "" -#: netbox/core/forms/model_forms.py:54 netbox/core/tables/data.py:46 -#: netbox/templates/core/datafile.html:27 -#: netbox/templates/extras/report/base.html:33 -#: netbox/templates/extras/script/base.html:32 +#: core/forms/model_forms.py:55 core/tables/data.py:52 +#: templates/core/datafile.html:27 templates/extras/report/base.html:33 +#: templates/extras/script/base.html:32 msgid "Source" msgstr "" -#: netbox/core/forms/model_forms.py:58 +#: core/forms/model_forms.py:57 templates/core/datasource.html:14 +#: templates/core/datasource.html:20 utilities/templates/buttons/sync.html:5 +msgid "Sync" +msgstr "" + +#: core/forms/model_forms.py:61 msgid "Backend Parameters" msgstr "" -#: netbox/core/forms/model_forms.py:96 +#: core/forms/model_forms.py:99 msgid "File Upload" msgstr "" -#: netbox/core/forms/model_forms.py:108 +#: core/forms/model_forms.py:111 msgid "Cannot upload a file and sync from an existing file" msgstr "" -#: netbox/core/forms/model_forms.py:110 +#: core/forms/model_forms.py:113 msgid "Must upload a file or select a data file to sync" msgstr "" -#: netbox/core/forms/model_forms.py:153 -#: netbox/templates/dcim/rack_elevation_list.html:6 +#: core/forms/model_forms.py:156 templates/dcim/rack_elevation_list.html:6 msgid "Rack Elevations" msgstr "" -#: netbox/core/forms/model_forms.py:157 netbox/dcim/choices.py:1530 -#: netbox/dcim/forms/bulk_edit.py:987 netbox/dcim/forms/bulk_edit.py:1375 -#: netbox/dcim/forms/bulk_edit.py:1393 netbox/dcim/tables/racks.py:157 -#: netbox/netbox/navigation/menu.py:312 netbox/netbox/navigation/menu.py:316 +#: core/forms/model_forms.py:160 dcim/choices.py:1533 +#: dcim/forms/bulk_edit.py:1031 dcim/forms/bulk_edit.py:1419 +#: dcim/forms/bulk_edit.py:1440 dcim/tables/racks.py:161 +#: netbox/navigation/menu.py:313 netbox/navigation/menu.py:317 msgid "Power" msgstr "" -#: netbox/core/forms/model_forms.py:159 netbox/netbox/navigation/menu.py:160 -#: netbox/templates/core/inc/config_data.html:37 +#: core/forms/model_forms.py:162 netbox/navigation/menu.py:161 +#: templates/core/inc/config_data.html:37 msgid "IPAM" msgstr "" -#: netbox/core/forms/model_forms.py:160 netbox/netbox/navigation/menu.py:238 -#: netbox/templates/core/inc/config_data.html:50 -#: netbox/vpn/forms/bulk_edit.py:77 netbox/vpn/forms/filtersets.py:47 -#: netbox/vpn/forms/model_forms.py:62 netbox/vpn/forms/model_forms.py:147 +#: core/forms/model_forms.py:163 netbox/navigation/menu.py:239 +#: templates/core/inc/config_data.html:50 vpn/forms/bulk_edit.py:77 +#: vpn/forms/filtersets.py:47 vpn/forms/model_forms.py:62 +#: vpn/forms/model_forms.py:147 msgid "Security" msgstr "" -#: netbox/core/forms/model_forms.py:161 -#: netbox/templates/core/inc/config_data.html:59 +#: core/forms/model_forms.py:164 templates/core/inc/config_data.html:59 msgid "Banners" msgstr "" -#: netbox/core/forms/model_forms.py:162 -#: netbox/templates/core/inc/config_data.html:80 +#: core/forms/model_forms.py:165 templates/core/inc/config_data.html:80 msgid "Pagination" msgstr "" -#: netbox/core/forms/model_forms.py:163 netbox/extras/forms/bulk_edit.py:92 -#: netbox/extras/forms/filtersets.py:48 netbox/extras/forms/model_forms.py:116 -#: netbox/extras/forms/model_forms.py:129 -#: netbox/templates/core/inc/config_data.html:93 +#: core/forms/model_forms.py:166 extras/forms/bulk_edit.py:93 +#: extras/forms/filtersets.py:49 extras/forms/model_forms.py:119 +#: extras/forms/model_forms.py:132 templates/core/inc/config_data.html:93 msgid "Validation" msgstr "" -#: netbox/core/forms/model_forms.py:164 -#: netbox/templates/account/preferences.html:6 +#: core/forms/model_forms.py:167 templates/account/preferences.html:6 msgid "User Preferences" msgstr "" -#: netbox/core/forms/model_forms.py:167 netbox/dcim/forms/filtersets.py:733 -#: netbox/templates/core/inc/config_data.html:127 -#: netbox/users/forms/model_forms.py:64 +#: core/forms/model_forms.py:170 dcim/forms/filtersets.py:752 +#: templates/core/inc/config_data.html:127 users/forms/model_forms.py:64 msgid "Miscellaneous" msgstr "" -#: netbox/core/forms/model_forms.py:169 +#: core/forms/model_forms.py:172 msgid "Config Revision" msgstr "" -#: netbox/core/forms/model_forms.py:208 +#: core/forms/model_forms.py:211 msgid "This parameter has been defined statically and cannot be modified." msgstr "" -#: netbox/core/forms/model_forms.py:216 +#: core/forms/model_forms.py:219 #, python-brace-format msgid "Current value: {value}" msgstr "" -#: netbox/core/forms/model_forms.py:218 +#: core/forms/model_forms.py:221 msgid " (default)" msgstr "" -#: netbox/core/models/change_logging.py:29 +#: core/models/change_logging.py:29 msgid "time" msgstr "" -#: netbox/core/models/change_logging.py:42 +#: core/models/change_logging.py:42 msgid "user name" msgstr "" -#: netbox/core/models/change_logging.py:47 +#: core/models/change_logging.py:47 msgid "request ID" msgstr "" -#: netbox/core/models/change_logging.py:52 netbox/extras/models/staging.py:77 +#: core/models/change_logging.py:52 msgid "action" msgstr "" -#: netbox/core/models/change_logging.py:86 +#: core/models/change_logging.py:86 msgid "pre-change data" msgstr "" -#: netbox/core/models/change_logging.py:92 +#: core/models/change_logging.py:92 msgid "post-change data" msgstr "" -#: netbox/core/models/change_logging.py:106 +#: core/models/change_logging.py:106 msgid "object change" msgstr "" -#: netbox/core/models/change_logging.py:107 +#: core/models/change_logging.py:107 msgid "object changes" msgstr "" -#: netbox/core/models/change_logging.py:123 +#: core/models/change_logging.py:123 #, python-brace-format msgid "Change logging is not supported for this object type ({type})." msgstr "" -#: netbox/core/models/config.py:18 netbox/core/models/data.py:263 -#: netbox/core/models/files.py:27 netbox/core/models/jobs.py:52 -#: netbox/extras/models/models.py:733 netbox/extras/models/notifications.py:39 -#: netbox/extras/models/notifications.py:186 -#: netbox/netbox/models/features.py:53 netbox/users/models/tokens.py:32 +#: core/models/config.py:18 core/models/data.py:269 core/models/files.py:30 +#: core/models/jobs.py:52 extras/models/models.py:806 +#: extras/models/notifications.py:39 extras/models/notifications.py:186 +#: netbox/models/features.py:53 users/models/tokens.py:32 msgid "created" msgstr "" -#: netbox/core/models/config.py:22 +#: core/models/config.py:22 msgid "comment" msgstr "" -#: netbox/core/models/config.py:29 +#: core/models/config.py:29 msgid "configuration data" msgstr "" -#: netbox/core/models/config.py:36 +#: core/models/config.py:36 msgid "config revision" msgstr "" -#: netbox/core/models/config.py:37 +#: core/models/config.py:37 msgid "config revisions" msgstr "" -#: netbox/core/models/config.py:41 +#: core/models/config.py:41 msgid "Default configuration" msgstr "" -#: netbox/core/models/config.py:43 +#: core/models/config.py:43 msgid "Current configuration" msgstr "" -#: netbox/core/models/config.py:44 +#: core/models/config.py:44 #, python-brace-format msgid "Config revision #{id}" msgstr "" -#: netbox/core/models/data.py:44 netbox/dcim/models/cables.py:42 -#: netbox/dcim/models/device_component_templates.py:199 -#: netbox/dcim/models/device_component_templates.py:234 -#: netbox/dcim/models/device_component_templates.py:270 -#: netbox/dcim/models/device_component_templates.py:335 -#: netbox/dcim/models/device_component_templates.py:420 -#: netbox/dcim/models/device_component_templates.py:526 -#: netbox/dcim/models/device_component_templates.py:626 -#: netbox/dcim/models/device_components.py:282 -#: netbox/dcim/models/device_components.py:309 -#: netbox/dcim/models/device_components.py:340 -#: netbox/dcim/models/device_components.py:456 -#: netbox/dcim/models/device_components.py:656 -#: netbox/dcim/models/device_components.py:1024 -#: netbox/dcim/models/device_components.py:1095 netbox/dcim/models/power.py:100 -#: netbox/extras/models/customfields.py:80 netbox/extras/models/search.py:41 -#: netbox/virtualization/models/clusters.py:57 netbox/vpn/models/l2vpn.py:32 +#: core/models/data.py:44 dcim/models/cables.py:42 +#: dcim/models/device_component_templates.py:199 +#: dcim/models/device_component_templates.py:234 +#: dcim/models/device_component_templates.py:270 +#: dcim/models/device_component_templates.py:335 +#: dcim/models/device_component_templates.py:420 +#: dcim/models/device_component_templates.py:526 +#: dcim/models/device_component_templates.py:626 +#: dcim/models/device_components.py:282 dcim/models/device_components.py:309 +#: dcim/models/device_components.py:340 dcim/models/device_components.py:462 +#: dcim/models/device_components.py:665 dcim/models/device_components.py:1033 +#: dcim/models/device_components.py:1104 dcim/models/power.py:100 +#: extras/models/customfields.py:80 extras/models/search.py:41 +#: virtualization/models/clusters.py:57 vpn/models/l2vpn.py:31 msgid "type" msgstr "" -#: netbox/core/models/data.py:49 netbox/extras/choices.py:37 -#: netbox/extras/models/models.py:164 netbox/extras/tables/tables.py:679 -#: netbox/templates/core/datasource.html:58 -#: netbox/templates/core/plugin.html:66 +#: core/models/data.py:49 extras/choices.py:37 extras/models/models.py:166 +#: extras/tables/tables.py:734 templates/core/datasource.html:62 +#: templates/core/plugin.html:66 msgid "URL" msgstr "" -#: netbox/core/models/data.py:59 -#: netbox/dcim/models/device_component_templates.py:425 -#: netbox/dcim/models/device_components.py:508 -#: netbox/extras/models/models.py:70 netbox/extras/models/models.py:301 -#: netbox/extras/models/models.py:526 netbox/users/models/permissions.py:29 +#: core/models/data.py:59 dcim/models/device_component_templates.py:425 +#: dcim/models/device_components.py:517 extras/models/models.py:72 +#: extras/models/models.py:303 extras/models/models.py:484 +#: extras/models/models.py:563 users/models/permissions.py:28 msgid "enabled" msgstr "" -#: netbox/core/models/data.py:63 +#: core/models/data.py:63 +msgid "sync interval" +msgstr "" + +#: core/models/data.py:69 msgid "ignore rules" msgstr "" -#: netbox/core/models/data.py:65 +#: core/models/data.py:71 msgid "Patterns (one per line) matching files to ignore when syncing" msgstr "" -#: netbox/core/models/data.py:68 netbox/extras/models/models.py:534 +#: core/models/data.py:74 extras/models/models.py:492 msgid "parameters" msgstr "" -#: netbox/core/models/data.py:73 +#: core/models/data.py:79 msgid "last synced" msgstr "" -#: netbox/core/models/data.py:81 +#: core/models/data.py:87 msgid "data source" msgstr "" -#: netbox/core/models/data.py:82 +#: core/models/data.py:88 msgid "data sources" msgstr "" -#: netbox/core/models/data.py:119 +#: core/models/data.py:125 #, python-brace-format msgid "Unknown backend type: {type}" msgstr "" -#: netbox/core/models/data.py:161 +#: core/models/data.py:167 msgid "Cannot initiate sync; syncing already in progress." msgstr "" -#: netbox/core/models/data.py:174 +#: core/models/data.py:180 msgid "" "There was an error initializing the backend. A dependency needs to be " "installed: " msgstr "" -#: netbox/core/models/data.py:267 netbox/core/models/files.py:31 -#: netbox/netbox/models/features.py:59 +#: core/models/data.py:273 core/models/files.py:34 netbox/models/features.py:59 msgid "last updated" msgstr "" -#: netbox/core/models/data.py:277 netbox/dcim/models/cables.py:444 +#: core/models/data.py:283 dcim/models/cables.py:440 msgid "path" msgstr "" -#: netbox/core/models/data.py:280 +#: core/models/data.py:286 msgid "File path relative to the data source's root" msgstr "" -#: netbox/core/models/data.py:284 netbox/ipam/models/ip.py:489 +#: core/models/data.py:290 ipam/models/ip.py:492 msgid "size" msgstr "" -#: netbox/core/models/data.py:287 +#: core/models/data.py:293 msgid "hash" msgstr "" -#: netbox/core/models/data.py:291 +#: core/models/data.py:297 msgid "Length must be 64 hexadecimal characters." msgstr "" -#: netbox/core/models/data.py:293 +#: core/models/data.py:299 msgid "SHA256 hash of the file data" msgstr "" -#: netbox/core/models/data.py:310 +#: core/models/data.py:313 msgid "data file" msgstr "" -#: netbox/core/models/data.py:311 +#: core/models/data.py:314 msgid "data files" msgstr "" -#: netbox/core/models/data.py:398 +#: core/models/data.py:387 msgid "auto sync record" msgstr "" -#: netbox/core/models/data.py:399 +#: core/models/data.py:388 msgid "auto sync records" msgstr "" -#: netbox/core/models/files.py:37 +#: core/models/files.py:40 msgid "file root" msgstr "" -#: netbox/core/models/files.py:42 +#: core/models/files.py:45 msgid "file path" msgstr "" -#: netbox/core/models/files.py:44 +#: core/models/files.py:47 msgid "File path relative to the designated root path" msgstr "" -#: netbox/core/models/files.py:61 +#: core/models/files.py:61 msgid "managed file" msgstr "" -#: netbox/core/models/files.py:62 +#: core/models/files.py:62 msgid "managed files" msgstr "" -#: netbox/core/models/files.py:100 +#: core/models/files.py:120 #, python-brace-format msgid "A {model} with this file path already exists ({path})." msgstr "" -#: netbox/core/models/jobs.py:56 +#: core/models/jobs.py:56 msgid "scheduled" msgstr "" -#: netbox/core/models/jobs.py:61 +#: core/models/jobs.py:61 msgid "interval" msgstr "" -#: netbox/core/models/jobs.py:67 +#: core/models/jobs.py:67 msgid "Recurrence interval (in minutes)" msgstr "" -#: netbox/core/models/jobs.py:70 +#: core/models/jobs.py:70 msgid "started" msgstr "" -#: netbox/core/models/jobs.py:75 +#: core/models/jobs.py:75 msgid "completed" msgstr "" -#: netbox/core/models/jobs.py:93 netbox/extras/models/models.py:101 -#: netbox/extras/models/staging.py:95 +#: core/models/jobs.py:93 extras/models/models.py:103 msgid "data" msgstr "" -#: netbox/core/models/jobs.py:99 +#: core/models/jobs.py:99 msgid "error" msgstr "" -#: netbox/core/models/jobs.py:104 +#: core/models/jobs.py:104 msgid "job ID" msgstr "" -#: netbox/core/models/jobs.py:115 +#: core/models/jobs.py:115 msgid "job" msgstr "" -#: netbox/core/models/jobs.py:116 +#: core/models/jobs.py:116 msgid "jobs" msgstr "" -#: netbox/core/models/jobs.py:139 +#: core/models/jobs.py:139 #, python-brace-format msgid "Jobs cannot be assigned to this object type ({type})." msgstr "" -#: netbox/core/models/jobs.py:193 +#: core/models/jobs.py:193 #, python-brace-format msgid "Invalid status for job termination. Choices are: {choices}" msgstr "" -#: netbox/core/models/jobs.py:234 +#: core/models/jobs.py:234 msgid "" "enqueue() cannot be called with values for both schedule_at and immediate." msgstr "" -#: netbox/core/signals.py:126 +#: core/signals.py:125 #, python-brace-format msgid "Deletion is prevented by a protection rule: {message}" msgstr "" -#: netbox/core/tables/change_logging.py:25 -#: netbox/templates/account/profile.html:19 netbox/templates/users/user.html:21 +#: core/tables/change_logging.py:25 templates/account/profile.html:19 +#: templates/users/user.html:21 msgid "Full Name" msgstr "" -#: netbox/core/tables/change_logging.py:37 netbox/core/tables/jobs.py:21 -#: netbox/extras/choices.py:41 netbox/extras/tables/tables.py:282 -#: netbox/extras/tables/tables.py:300 netbox/extras/tables/tables.py:332 -#: netbox/extras/tables/tables.py:412 netbox/extras/tables/tables.py:473 -#: netbox/extras/tables/tables.py:582 netbox/extras/tables/tables.py:622 -#: netbox/extras/tables/tables.py:676 netbox/netbox/tables/tables.py:247 -#: netbox/templates/core/objectchange.html:58 -#: netbox/templates/extras/eventrule.html:78 -#: netbox/templates/extras/journalentry.html:18 -#: netbox/tenancy/tables/contacts.py:93 netbox/vpn/tables/l2vpn.py:64 +#: core/tables/change_logging.py:37 core/tables/jobs.py:21 extras/choices.py:41 +#: extras/tables/tables.py:323 extras/tables/tables.py:341 +#: extras/tables/tables.py:373 extras/tables/tables.py:453 +#: extras/tables/tables.py:514 extras/tables/tables.py:637 +#: extras/tables/tables.py:677 extras/tables/tables.py:731 +#: netbox/tables/tables.py:273 templates/core/objectchange.html:58 +#: templates/extras/eventrule.html:78 templates/extras/journalentry.html:18 +#: tenancy/tables/contacts.py:97 vpn/tables/l2vpn.py:67 msgid "Object" msgstr "" -#: netbox/core/tables/change_logging.py:42 -#: netbox/templates/core/objectchange.html:68 +#: core/tables/change_logging.py:42 templates/core/objectchange.html:68 msgid "Request ID" msgstr "" -#: netbox/core/tables/config.py:21 netbox/users/forms/filtersets.py:44 -#: netbox/users/tables.py:39 +#: core/tables/config.py:21 users/forms/filtersets.py:44 users/tables.py:39 msgid "Is Active" msgstr "" -#: netbox/core/tables/data.py:50 netbox/templates/core/datafile.html:31 +#: core/tables/data.py:32 +msgid "Last Synced" +msgstr "" + +#: core/tables/data.py:35 templates/core/datasource.html:118 +msgid "Files" +msgstr "" + +#: core/tables/data.py:56 templates/core/datafile.html:31 msgid "Path" msgstr "" -#: netbox/core/tables/data.py:54 -#: netbox/templates/extras/inc/result_pending.html:7 +#: core/tables/data.py:60 templates/extras/inc/result_pending.html:7 msgid "Last updated" msgstr "" -#: netbox/core/tables/jobs.py:10 netbox/core/tables/tasks.py:76 -#: netbox/dcim/tables/devicetypes.py:169 netbox/extras/tables/tables.py:219 -#: netbox/extras/tables/tables.py:463 netbox/extras/tables/tables.py:647 -#: netbox/netbox/tables/tables.py:192 -#: netbox/templates/dcim/virtualchassis_edit.html:56 -#: netbox/utilities/forms/forms.py:73 netbox/wireless/tables/wirelesslink.py:16 +#: core/tables/jobs.py:10 core/tables/tasks.py:76 +#: dcim/tables/devicetypes.py:169 extras/tables/tables.py:230 +#: extras/tables/tables.py:504 extras/tables/tables.py:702 +#: netbox/tables/tables.py:218 templates/dcim/virtualchassis_edit.html:56 +#: utilities/forms/forms.py:73 wireless/tables/wirelesslink.py:16 msgid "ID" msgstr "" -#: netbox/core/tables/jobs.py:35 +#: core/tables/jobs.py:35 msgid "Interval" msgstr "" -#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 -#: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 -#: netbox/vpn/tables/crypto.py:61 +#: core/tables/plugins.py:23 templates/vpn/ipsecprofile.html:44 +#: vpn/forms/bulk_edit.py:141 vpn/forms/bulk_import.py:172 +#: vpn/tables/crypto.py:61 msgid "Version" msgstr "" -#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 +#: core/tables/plugins.py:28 templates/core/datafile.html:38 msgid "Last Updated" msgstr "" -#: netbox/core/tables/plugins.py:29 +#: core/tables/plugins.py:32 msgid "Minimum NetBox Version" msgstr "" -#: netbox/core/tables/plugins.py:33 +#: core/tables/plugins.py:36 msgid "Maximum NetBox Version" msgstr "" -#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 +#: core/tables/plugins.py:40 core/tables/plugins.py:86 msgid "No plugin data found" msgstr "" -#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 +#: core/tables/plugins.py:57 templates/core/plugin.html:62 msgid "Author" msgstr "" -#: netbox/core/tables/plugins.py:60 -msgid "Installed" -msgstr "" - -#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 +#: core/tables/plugins.py:69 templates/core/plugin.html:84 msgid "Certified" msgstr "" -#: netbox/core/tables/plugins.py:66 +#: core/tables/plugins.py:72 msgid "Published" msgstr "" -#: netbox/core/tables/plugins.py:72 +#: core/tables/plugins.py:78 msgid "Installed Version" msgstr "" -#: netbox/core/tables/plugins.py:76 +#: core/tables/plugins.py:82 msgid "Latest Version" msgstr "" -#: netbox/core/tables/tasks.py:18 +#: core/tables/tasks.py:18 msgid "Oldest Task" msgstr "" -#: netbox/core/tables/tasks.py:42 netbox/templates/core/rq_worker_list.html:39 +#: core/tables/tasks.py:42 templates/core/rq_worker_list.html:39 msgid "Workers" msgstr "" -#: netbox/core/tables/tasks.py:46 netbox/vpn/tables/tunnels.py:88 +#: core/tables/tasks.py:46 vpn/tables/tunnels.py:88 msgid "Host" msgstr "" -#: netbox/core/tables/tasks.py:50 netbox/ipam/forms/filtersets.py:601 +#: core/tables/tasks.py:50 ipam/forms/filtersets.py:609 msgid "Port" msgstr "" -#: netbox/core/tables/tasks.py:54 +#: core/tables/tasks.py:54 msgid "DB" msgstr "" -#: netbox/core/tables/tasks.py:58 +#: core/tables/tasks.py:58 msgid "Scheduler PID" msgstr "" -#: netbox/core/tables/tasks.py:62 +#: core/tables/tasks.py:62 msgid "No queues found" msgstr "" -#: netbox/core/tables/tasks.py:82 +#: core/tables/tasks.py:82 msgid "Enqueued" msgstr "" -#: netbox/core/tables/tasks.py:85 +#: core/tables/tasks.py:85 msgid "Ended" msgstr "" -#: netbox/core/tables/tasks.py:93 netbox/templates/core/rq_task.html:85 +#: core/tables/tasks.py:93 templates/core/rq_task.html:85 msgid "Callable" msgstr "" -#: netbox/core/tables/tasks.py:97 +#: core/tables/tasks.py:97 msgid "No tasks found" msgstr "" -#: netbox/core/tables/tasks.py:118 netbox/templates/core/rq_worker.html:47 +#: core/tables/tasks.py:118 templates/core/rq_worker.html:47 msgid "State" msgstr "" -#: netbox/core/tables/tasks.py:121 netbox/templates/core/rq_worker.html:51 +#: core/tables/tasks.py:121 templates/core/rq_worker.html:51 msgid "Birth" msgstr "" -#: netbox/core/tables/tasks.py:124 netbox/templates/core/rq_worker.html:59 +#: core/tables/tasks.py:124 templates/core/rq_worker.html:59 msgid "PID" msgstr "" -#: netbox/core/tables/tasks.py:128 +#: core/tables/tasks.py:128 msgid "No workers found" msgstr "" -#: netbox/core/utils.py:84 netbox/core/utils.py:150 netbox/core/views.py:396 +#: core/utils.py:84 core/utils.py:150 core/views.py:397 #, python-brace-format msgid "Job {job_id} not found" msgstr "" -#: netbox/core/utils.py:102 netbox/core/utils.py:118 +#: core/utils.py:102 core/utils.py:118 #, python-brace-format msgid "Job {id} not found." msgstr "" -#: netbox/core/views.py:88 +#: core/views.py:89 #, python-brace-format msgid "Queued job #{id} to sync {datasource}" msgstr "" -#: netbox/core/views.py:332 +#: core/views.py:333 #, python-brace-format msgid "Restored configuration revision #{id}" msgstr "" -#: netbox/core/views.py:435 +#: core/views.py:436 #, python-brace-format msgid "Job {id} has been deleted." msgstr "" -#: netbox/core/views.py:437 +#: core/views.py:438 #, python-brace-format msgid "Error deleting job {id}: {error}" msgstr "" -#: netbox/core/views.py:446 +#: core/views.py:447 #, python-brace-format msgid "Job {id} has been re-enqueued." msgstr "" -#: netbox/core/views.py:455 +#: core/views.py:456 #, python-brace-format msgid "Job {id} has been enqueued." msgstr "" -#: netbox/core/views.py:464 +#: core/views.py:465 #, python-brace-format msgid "Job {id} has been stopped." msgstr "" -#: netbox/core/views.py:466 +#: core/views.py:467 #, python-brace-format msgid "Failed to stop job {id}" msgstr "" -#: netbox/core/views.py:601 +#: core/views.py:602 msgid "Plugins catalog could not be loaded" msgstr "" -#: netbox/core/views.py:635 +#: core/views.py:638 #, python-brace-format msgid "Plugin {name} not found" msgstr "" -#: netbox/dcim/api/serializers_/device_components.py:262 +#: dcim/api/serializers_/device_components.py:263 msgid "Interface mode does not support q-in-q service vlan" msgstr "" -#: netbox/dcim/api/serializers_/device_components.py:269 +#: dcim/api/serializers_/device_components.py:270 msgid "Interface mode does not support untagged vlan" msgstr "" -#: netbox/dcim/api/serializers_/device_components.py:274 -#: netbox/dcim/api/serializers_/device_components.py:279 +#: dcim/api/serializers_/device_components.py:275 +#: dcim/api/serializers_/device_components.py:280 msgid "Interface mode does not support tagged vlans" msgstr "" -#: netbox/dcim/api/serializers_/devices.py:53 -#: netbox/dcim/api/serializers_/devicetypes.py:26 +#: dcim/api/serializers_/devices.py:53 dcim/api/serializers_/devicetypes.py:27 msgid "Position (U)" msgstr "" -#: netbox/dcim/api/serializers_/racks.py:113 netbox/templates/dcim/rack.html:28 +#: dcim/api/serializers_/racks.py:113 templates/dcim/rack.html:28 msgid "Facility ID" msgstr "" -#: netbox/dcim/choices.py:21 netbox/virtualization/choices.py:21 +#: dcim/choices.py:21 virtualization/choices.py:21 msgid "Staging" msgstr "" -#: netbox/dcim/choices.py:23 netbox/dcim/choices.py:189 -#: netbox/dcim/choices.py:240 netbox/dcim/choices.py:1543 -#: netbox/dcim/choices.py:1675 netbox/virtualization/choices.py:23 -#: netbox/virtualization/choices.py:49 +#: dcim/choices.py:23 dcim/choices.py:190 dcim/choices.py:242 +#: dcim/choices.py:1546 dcim/choices.py:1695 virtualization/choices.py:23 +#: virtualization/choices.py:49 vpn/choices.py:282 msgid "Decommissioning" msgstr "" -#: netbox/dcim/choices.py:24 +#: dcim/choices.py:24 msgid "Retired" msgstr "" -#: netbox/dcim/choices.py:65 +#: dcim/choices.py:65 msgid "2-post frame" msgstr "" -#: netbox/dcim/choices.py:66 +#: dcim/choices.py:66 msgid "4-post frame" msgstr "" -#: netbox/dcim/choices.py:67 +#: dcim/choices.py:67 msgid "4-post cabinet" msgstr "" -#: netbox/dcim/choices.py:68 +#: dcim/choices.py:68 msgid "Wall-mounted frame" msgstr "" -#: netbox/dcim/choices.py:69 +#: dcim/choices.py:69 msgid "Wall-mounted frame (vertical)" msgstr "" -#: netbox/dcim/choices.py:70 +#: dcim/choices.py:70 msgid "Wall-mounted cabinet" msgstr "" -#: netbox/dcim/choices.py:71 +#: dcim/choices.py:71 msgid "Wall-mounted cabinet (vertical)" msgstr "" -#: netbox/dcim/choices.py:83 netbox/dcim/choices.py:84 -#: netbox/dcim/choices.py:85 netbox/dcim/choices.py:86 +#: dcim/choices.py:83 dcim/choices.py:84 dcim/choices.py:85 dcim/choices.py:86 #, python-brace-format msgid "{n} inches" msgstr "" -#: netbox/dcim/choices.py:100 netbox/ipam/choices.py:32 -#: netbox/ipam/choices.py:50 netbox/ipam/choices.py:70 -#: netbox/ipam/choices.py:155 netbox/wireless/choices.py:26 +#: dcim/choices.py:100 ipam/choices.py:32 ipam/choices.py:50 ipam/choices.py:70 +#: ipam/choices.py:155 wireless/choices.py:26 msgid "Reserved" msgstr "" -#: netbox/dcim/choices.py:101 netbox/templates/dcim/device.html:259 +#: dcim/choices.py:101 templates/dcim/device.html:259 msgid "Available" msgstr "" -#: netbox/dcim/choices.py:104 netbox/ipam/choices.py:33 -#: netbox/ipam/choices.py:51 netbox/ipam/choices.py:71 -#: netbox/ipam/choices.py:156 netbox/wireless/choices.py:28 +#: dcim/choices.py:104 ipam/choices.py:33 ipam/choices.py:51 ipam/choices.py:71 +#: ipam/choices.py:156 wireless/choices.py:28 msgid "Deprecated" msgstr "" -#: netbox/dcim/choices.py:114 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:41 +#: dcim/choices.py:114 templates/dcim/inc/panels/racktype_dimensions.html:51 msgid "Millimeters" msgstr "" -#: netbox/dcim/choices.py:115 netbox/dcim/choices.py:1565 +#: dcim/choices.py:115 dcim/choices.py:1568 msgid "Inches" msgstr "" -#: netbox/dcim/choices.py:136 netbox/dcim/choices.py:207 -#: netbox/dcim/choices.py:254 +#: dcim/choices.py:137 dcim/choices.py:209 dcim/choices.py:257 msgid "Front to rear" msgstr "" -#: netbox/dcim/choices.py:137 netbox/dcim/choices.py:208 -#: netbox/dcim/choices.py:255 +#: dcim/choices.py:138 dcim/choices.py:210 dcim/choices.py:258 msgid "Rear to front" msgstr "" -#: netbox/dcim/choices.py:151 netbox/dcim/forms/bulk_edit.py:72 -#: netbox/dcim/forms/bulk_edit.py:91 netbox/dcim/forms/bulk_edit.py:177 -#: netbox/dcim/forms/bulk_edit.py:1423 netbox/dcim/forms/bulk_import.py:62 -#: netbox/dcim/forms/bulk_import.py:76 netbox/dcim/forms/bulk_import.py:139 -#: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 -#: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 -#: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 -#: netbox/dcim/forms/model_forms.py:1582 netbox/dcim/forms/object_import.py:177 -#: netbox/dcim/tables/devices.py:689 netbox/dcim/tables/devices.py:899 -#: netbox/dcim/tables/devices.py:986 netbox/dcim/tables/devices.py:1146 -#: netbox/extras/tables/tables.py:226 netbox/ipam/tables/fhrp.py:59 -#: netbox/ipam/tables/ip.py:330 netbox/ipam/tables/services.py:44 -#: netbox/templates/dcim/interface.html:108 -#: netbox/templates/dcim/interface.html:366 -#: netbox/templates/dcim/location.html:41 netbox/templates/dcim/region.html:37 -#: netbox/templates/dcim/sitegroup.html:37 -#: netbox/templates/ipam/service.html:28 -#: netbox/templates/tenancy/contactgroup.html:29 -#: netbox/templates/tenancy/tenantgroup.html:37 -#: netbox/templates/virtualization/vminterface.html:39 -#: netbox/templates/wireless/wirelesslangroup.html:37 -#: netbox/tenancy/forms/bulk_edit.py:27 netbox/tenancy/forms/bulk_edit.py:61 -#: netbox/tenancy/forms/bulk_import.py:24 -#: netbox/tenancy/forms/bulk_import.py:58 -#: netbox/tenancy/forms/model_forms.py:25 -#: netbox/tenancy/forms/model_forms.py:68 -#: netbox/virtualization/forms/bulk_edit.py:189 -#: netbox/virtualization/forms/bulk_import.py:157 -#: netbox/virtualization/tables/virtualmachines.py:132 -#: netbox/wireless/forms/bulk_edit.py:26 -#: netbox/wireless/forms/bulk_import.py:23 -#: netbox/wireless/forms/model_forms.py:22 +#: dcim/choices.py:152 dcim/forms/bulk_edit.py:75 dcim/forms/bulk_edit.py:95 +#: dcim/forms/bulk_edit.py:182 dcim/forms/bulk_edit.py:651 +#: dcim/forms/bulk_edit.py:1470 dcim/forms/bulk_import.py:63 +#: dcim/forms/bulk_import.py:77 dcim/forms/bulk_import.py:140 +#: dcim/forms/bulk_import.py:480 dcim/forms/bulk_import.py:624 +#: dcim/forms/bulk_import.py:894 dcim/forms/bulk_import.py:1149 +#: dcim/forms/filtersets.py:236 dcim/forms/filtersets.py:709 +#: dcim/forms/model_forms.py:79 dcim/forms/model_forms.py:99 +#: dcim/forms/model_forms.py:179 dcim/forms/model_forms.py:517 +#: dcim/forms/model_forms.py:1207 dcim/forms/model_forms.py:1676 +#: dcim/forms/object_import.py:177 dcim/tables/devices.py:696 +#: dcim/tables/devices.py:906 dcim/tables/devices.py:993 +#: dcim/tables/devices.py:1153 extras/tables/tables.py:237 +#: ipam/forms/bulk_import.py:568 ipam/forms/model_forms.py:768 +#: ipam/tables/fhrp.py:59 ipam/tables/ip.py:336 ipam/tables/services.py:44 +#: templates/dcim/devicerole.html:34 templates/dcim/interface.html:108 +#: templates/dcim/interface.html:366 templates/dcim/location.html:41 +#: templates/dcim/region.html:37 templates/dcim/sitegroup.html:37 +#: templates/ipam/service.html:30 templates/tenancy/contactgroup.html:29 +#: templates/tenancy/tenantgroup.html:37 +#: templates/virtualization/vminterface.html:39 +#: templates/wireless/wirelesslangroup.html:37 tenancy/forms/bulk_edit.py:27 +#: tenancy/forms/bulk_edit.py:62 tenancy/forms/bulk_import.py:24 +#: tenancy/forms/bulk_import.py:58 tenancy/forms/model_forms.py:25 +#: tenancy/forms/model_forms.py:69 virtualization/forms/bulk_edit.py:189 +#: virtualization/forms/bulk_import.py:157 +#: virtualization/tables/virtualmachines.py:132 wireless/forms/bulk_edit.py:26 +#: wireless/forms/bulk_import.py:23 wireless/forms/model_forms.py:22 msgid "Parent" msgstr "" -#: netbox/dcim/choices.py:152 +#: dcim/choices.py:153 msgid "Child" msgstr "" -#: netbox/dcim/choices.py:166 netbox/templates/dcim/device.html:349 -#: netbox/templates/dcim/rack.html:133 -#: netbox/templates/dcim/rack_elevation_list.html:20 -#: netbox/templates/dcim/rackreservation.html:76 +#: dcim/choices.py:167 templates/dcim/device.html:349 +#: templates/dcim/rack.html:133 templates/dcim/rack_elevation_list.html:20 +#: templates/dcim/rackreservation.html:76 msgid "Front" msgstr "" -#: netbox/dcim/choices.py:167 netbox/templates/dcim/device.html:355 -#: netbox/templates/dcim/rack.html:139 -#: netbox/templates/dcim/rack_elevation_list.html:21 -#: netbox/templates/dcim/rackreservation.html:82 +#: dcim/choices.py:168 templates/dcim/device.html:355 +#: templates/dcim/rack.html:139 templates/dcim/rack_elevation_list.html:21 +#: templates/dcim/rackreservation.html:82 msgid "Rear" msgstr "" -#: netbox/dcim/choices.py:186 netbox/dcim/choices.py:238 -#: netbox/dcim/choices.py:1673 netbox/virtualization/choices.py:47 +#: dcim/choices.py:187 dcim/choices.py:240 dcim/choices.py:1693 +#: virtualization/choices.py:47 msgid "Staged" msgstr "" -#: netbox/dcim/choices.py:188 +#: dcim/choices.py:189 msgid "Inventory" msgstr "" -#: netbox/dcim/choices.py:209 netbox/dcim/choices.py:256 +#: dcim/choices.py:211 dcim/choices.py:259 msgid "Left to right" msgstr "" -#: netbox/dcim/choices.py:210 netbox/dcim/choices.py:257 +#: dcim/choices.py:212 dcim/choices.py:260 msgid "Right to left" msgstr "" -#: netbox/dcim/choices.py:211 netbox/dcim/choices.py:258 +#: dcim/choices.py:213 dcim/choices.py:261 msgid "Side to rear" msgstr "" -#: netbox/dcim/choices.py:212 +#: dcim/choices.py:214 msgid "Rear to side" msgstr "" -#: netbox/dcim/choices.py:213 +#: dcim/choices.py:215 msgid "Bottom to top" msgstr "" -#: netbox/dcim/choices.py:214 +#: dcim/choices.py:216 msgid "Top to bottom" msgstr "" -#: netbox/dcim/choices.py:215 netbox/dcim/choices.py:259 -#: netbox/dcim/choices.py:1309 +#: dcim/choices.py:217 dcim/choices.py:262 dcim/choices.py:1312 msgid "Passive" msgstr "" -#: netbox/dcim/choices.py:216 +#: dcim/choices.py:218 msgid "Mixed" msgstr "" -#: netbox/dcim/choices.py:484 netbox/dcim/choices.py:733 +#: dcim/choices.py:487 dcim/choices.py:736 msgid "NEMA (Non-locking)" msgstr "" -#: netbox/dcim/choices.py:506 netbox/dcim/choices.py:755 +#: dcim/choices.py:509 dcim/choices.py:758 msgid "NEMA (Locking)" msgstr "" -#: netbox/dcim/choices.py:530 netbox/dcim/choices.py:779 +#: dcim/choices.py:533 dcim/choices.py:782 msgid "California Style" msgstr "" -#: netbox/dcim/choices.py:538 +#: dcim/choices.py:541 msgid "International/ITA" msgstr "" -#: netbox/dcim/choices.py:573 netbox/dcim/choices.py:814 +#: dcim/choices.py:576 dcim/choices.py:817 msgid "Proprietary" msgstr "" -#: netbox/dcim/choices.py:581 netbox/dcim/choices.py:824 -#: netbox/dcim/choices.py:1223 netbox/dcim/choices.py:1225 -#: netbox/dcim/choices.py:1459 netbox/dcim/choices.py:1461 -#: netbox/netbox/navigation/menu.py:208 +#: dcim/choices.py:584 dcim/choices.py:827 dcim/choices.py:1226 +#: dcim/choices.py:1228 dcim/choices.py:1462 dcim/choices.py:1464 +#: netbox/navigation/menu.py:209 msgid "Other" msgstr "" -#: netbox/dcim/choices.py:787 +#: dcim/choices.py:790 msgid "ITA/International" msgstr "" -#: netbox/dcim/choices.py:854 +#: dcim/choices.py:857 msgid "Physical" msgstr "" -#: netbox/dcim/choices.py:855 netbox/dcim/choices.py:1025 +#: dcim/choices.py:858 dcim/choices.py:1028 msgid "Virtual" msgstr "" -#: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 -#: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 -#: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 -#: netbox/templates/dcim/interface.html:267 +#: dcim/choices.py:859 dcim/choices.py:1103 dcim/forms/bulk_edit.py:1625 +#: dcim/forms/filtersets.py:1408 dcim/forms/model_forms.py:1117 +#: dcim/forms/model_forms.py:1570 netbox/navigation/menu.py:147 +#: netbox/navigation/menu.py:151 templates/dcim/interface.html:267 msgid "Wireless" msgstr "" -#: netbox/dcim/choices.py:1023 +#: dcim/choices.py:1026 msgid "Virtual interfaces" msgstr "" -#: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 -#: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 -#: netbox/templates/virtualization/vminterface.html:43 -#: netbox/virtualization/forms/bulk_edit.py:194 -#: netbox/virtualization/forms/bulk_import.py:164 -#: netbox/virtualization/tables/virtualmachines.py:136 +#: dcim/choices.py:1029 dcim/forms/bulk_edit.py:1478 +#: dcim/forms/bulk_import.py:901 dcim/forms/model_forms.py:1099 +#: dcim/tables/devices.py:700 templates/dcim/interface.html:112 +#: templates/virtualization/vminterface.html:43 +#: virtualization/forms/bulk_edit.py:194 +#: virtualization/forms/bulk_import.py:164 +#: virtualization/tables/virtualmachines.py:136 msgid "Bridge" msgstr "" -#: netbox/dcim/choices.py:1027 +#: dcim/choices.py:1030 msgid "Link Aggregation Group (LAG)" msgstr "" -#: netbox/dcim/choices.py:1031 +#: dcim/choices.py:1034 msgid "Ethernet (fixed)" msgstr "" -#: netbox/dcim/choices.py:1047 +#: dcim/choices.py:1050 msgid "Ethernet (modular)" msgstr "" -#: netbox/dcim/choices.py:1084 +#: dcim/choices.py:1087 msgid "Ethernet (backplane)" msgstr "" -#: netbox/dcim/choices.py:1116 +#: dcim/choices.py:1119 msgid "Cellular" msgstr "" -#: netbox/dcim/choices.py:1168 netbox/dcim/forms/filtersets.py:384 -#: netbox/dcim/forms/filtersets.py:810 netbox/dcim/forms/filtersets.py:1012 -#: netbox/dcim/forms/filtersets.py:1606 -#: netbox/templates/dcim/inventoryitem.html:56 -#: netbox/templates/dcim/virtualchassis_edit.html:58 +#: dcim/choices.py:1171 dcim/forms/filtersets.py:385 +#: dcim/forms/filtersets.py:829 dcim/forms/filtersets.py:1031 +#: dcim/forms/filtersets.py:1630 templates/dcim/inventoryitem.html:56 +#: templates/dcim/virtualchassis_edit.html:58 msgid "Serial" msgstr "" -#: netbox/dcim/choices.py:1183 +#: dcim/choices.py:1186 msgid "Coaxial" msgstr "" -#: netbox/dcim/choices.py:1204 +#: dcim/choices.py:1207 msgid "Stacking" msgstr "" -#: netbox/dcim/choices.py:1254 +#: dcim/choices.py:1257 msgid "Half" msgstr "" -#: netbox/dcim/choices.py:1255 +#: dcim/choices.py:1258 msgid "Full" msgstr "" -#: netbox/dcim/choices.py:1256 netbox/netbox/preferences.py:31 -#: netbox/wireless/choices.py:480 +#: dcim/choices.py:1259 netbox/preferences.py:31 wireless/choices.py:480 msgid "Auto" msgstr "" -#: netbox/dcim/choices.py:1268 +#: dcim/choices.py:1271 msgid "Access" msgstr "" -#: netbox/dcim/choices.py:1269 netbox/ipam/tables/vlans.py:148 -#: netbox/ipam/tables/vlans.py:193 -#: netbox/templates/dcim/inc/interface_vlans_table.html:7 +#: dcim/choices.py:1272 ipam/tables/vlans.py:150 ipam/tables/vlans.py:195 +#: templates/dcim/inc/interface_vlans_table.html:7 msgid "Tagged" msgstr "" -#: netbox/dcim/choices.py:1270 +#: dcim/choices.py:1273 msgid "Tagged (All)" msgstr "" -#: netbox/dcim/choices.py:1271 netbox/templates/ipam/vlan_edit.html:26 +#: dcim/choices.py:1274 templates/ipam/vlan_edit.html:26 msgid "Q-in-Q (802.1ad)" msgstr "" -#: netbox/dcim/choices.py:1300 +#: dcim/choices.py:1303 msgid "IEEE Standard" msgstr "" -#: netbox/dcim/choices.py:1311 +#: dcim/choices.py:1314 msgid "Passive 24V (2-pair)" msgstr "" -#: netbox/dcim/choices.py:1312 +#: dcim/choices.py:1315 msgid "Passive 24V (4-pair)" msgstr "" -#: netbox/dcim/choices.py:1313 +#: dcim/choices.py:1316 msgid "Passive 48V (2-pair)" msgstr "" -#: netbox/dcim/choices.py:1314 +#: dcim/choices.py:1317 msgid "Passive 48V (4-pair)" msgstr "" -#: netbox/dcim/choices.py:1387 netbox/dcim/choices.py:1500 +#: dcim/choices.py:1390 dcim/choices.py:1503 msgid "Copper" msgstr "" -#: netbox/dcim/choices.py:1410 +#: dcim/choices.py:1413 msgid "Fiber Optic" msgstr "" -#: netbox/dcim/choices.py:1446 netbox/dcim/choices.py:1529 +#: dcim/choices.py:1449 dcim/choices.py:1532 msgid "USB" msgstr "" -#: netbox/dcim/choices.py:1516 +#: dcim/choices.py:1519 msgid "Fiber" msgstr "" -#: netbox/dcim/choices.py:1541 netbox/dcim/forms/filtersets.py:1276 +#: dcim/choices.py:1544 dcim/forms/filtersets.py:1295 msgid "Connected" msgstr "" -#: netbox/dcim/choices.py:1560 netbox/netbox/choices.py:175 +#: dcim/choices.py:1563 netbox/choices.py:175 msgid "Kilometers" msgstr "" -#: netbox/dcim/choices.py:1561 netbox/netbox/choices.py:176 -#: netbox/templates/dcim/cable_trace.html:65 +#: dcim/choices.py:1564 netbox/choices.py:176 +#: templates/dcim/cable_trace.html:65 msgid "Meters" msgstr "" -#: netbox/dcim/choices.py:1562 +#: dcim/choices.py:1565 msgid "Centimeters" msgstr "" -#: netbox/dcim/choices.py:1563 netbox/netbox/choices.py:177 +#: dcim/choices.py:1566 netbox/choices.py:177 msgid "Miles" msgstr "" -#: netbox/dcim/choices.py:1564 netbox/netbox/choices.py:178 -#: netbox/templates/dcim/cable_trace.html:66 +#: dcim/choices.py:1567 netbox/choices.py:178 +#: templates/dcim/cable_trace.html:66 msgid "Feet" msgstr "" -#: netbox/dcim/choices.py:1612 +#: dcim/choices.py:1615 msgid "Redundant" msgstr "" -#: netbox/dcim/choices.py:1633 +#: dcim/choices.py:1636 msgid "Single phase" msgstr "" -#: netbox/dcim/choices.py:1634 +#: dcim/choices.py:1637 msgid "Three-phase" msgstr "" -#: netbox/dcim/fields.py:45 +#: dcim/choices.py:1653 extras/choices.py:53 netbox/preferences.py:21 +#: templates/extras/customfield.html:78 vpn/choices.py:20 +#: wireless/choices.py:27 +msgid "Disabled" +msgstr "" + +#: dcim/choices.py:1654 +msgid "Faulty" +msgstr "" + +#: dcim/fields.py:45 #, python-brace-format msgid "Invalid MAC address format: {value}" msgstr "" -#: netbox/dcim/fields.py:71 +#: dcim/fields.py:71 #, python-brace-format msgid "Invalid WWN format: {value}" msgstr "" -#: netbox/dcim/filtersets.py:87 +#: dcim/filtersets.py:89 msgid "Parent region (ID)" msgstr "" -#: netbox/dcim/filtersets.py:93 +#: dcim/filtersets.py:95 msgid "Parent region (slug)" msgstr "" -#: netbox/dcim/filtersets.py:117 +#: dcim/filtersets.py:119 msgid "Parent site group (ID)" msgstr "" -#: netbox/dcim/filtersets.py:123 +#: dcim/filtersets.py:125 msgid "Parent site group (slug)" msgstr "" -#: netbox/dcim/filtersets.py:165 netbox/extras/filtersets.py:364 -#: netbox/ipam/filtersets.py:836 netbox/ipam/filtersets.py:988 +#: dcim/filtersets.py:167 extras/filtersets.py:422 ipam/filtersets.py:836 +#: ipam/filtersets.py:988 msgid "Group (ID)" msgstr "" -#: netbox/dcim/filtersets.py:171 +#: dcim/filtersets.py:173 msgid "Group (slug)" msgstr "" -#: netbox/dcim/filtersets.py:177 netbox/dcim/filtersets.py:182 +#: dcim/filtersets.py:179 dcim/filtersets.py:184 msgid "AS (ID)" msgstr "" -#: netbox/dcim/filtersets.py:247 +#: dcim/filtersets.py:249 msgid "Parent location (ID)" msgstr "" -#: netbox/dcim/filtersets.py:253 +#: dcim/filtersets.py:255 msgid "Parent location (slug)" msgstr "" -#: netbox/dcim/filtersets.py:297 netbox/dcim/filtersets.py:382 -#: netbox/dcim/filtersets.py:540 netbox/dcim/filtersets.py:679 -#: netbox/dcim/filtersets.py:883 netbox/dcim/filtersets.py:934 -#: netbox/dcim/filtersets.py:974 netbox/dcim/filtersets.py:1315 -#: netbox/dcim/filtersets.py:2036 +#: dcim/filtersets.py:299 dcim/filtersets.py:384 dcim/filtersets.py:542 +#: dcim/filtersets.py:707 dcim/filtersets.py:911 dcim/filtersets.py:985 +#: dcim/filtersets.py:1025 dcim/filtersets.py:1368 dcim/filtersets.py:2093 msgid "Manufacturer (ID)" msgstr "" -#: netbox/dcim/filtersets.py:303 netbox/dcim/filtersets.py:388 -#: netbox/dcim/filtersets.py:546 netbox/dcim/filtersets.py:685 -#: netbox/dcim/filtersets.py:889 netbox/dcim/filtersets.py:940 -#: netbox/dcim/filtersets.py:980 netbox/dcim/filtersets.py:1321 -#: netbox/dcim/filtersets.py:2042 +#: dcim/filtersets.py:305 dcim/filtersets.py:390 dcim/filtersets.py:548 +#: dcim/filtersets.py:713 dcim/filtersets.py:917 dcim/filtersets.py:991 +#: dcim/filtersets.py:1031 dcim/filtersets.py:1374 dcim/filtersets.py:2099 msgid "Manufacturer (slug)" msgstr "" -#: netbox/dcim/filtersets.py:394 +#: dcim/filtersets.py:396 msgid "Rack type (slug)" msgstr "" -#: netbox/dcim/filtersets.py:398 +#: dcim/filtersets.py:400 msgid "Rack type (ID)" msgstr "" -#: netbox/dcim/filtersets.py:412 netbox/dcim/filtersets.py:893 -#: netbox/dcim/filtersets.py:995 netbox/dcim/filtersets.py:2046 -#: netbox/ipam/filtersets.py:376 netbox/ipam/filtersets.py:488 -#: netbox/ipam/filtersets.py:998 netbox/virtualization/filtersets.py:176 +#: dcim/filtersets.py:414 dcim/filtersets.py:921 dcim/filtersets.py:1047 +#: dcim/filtersets.py:2103 ipam/filtersets.py:376 ipam/filtersets.py:488 +#: ipam/filtersets.py:998 virtualization/filtersets.py:177 msgid "Role (ID)" msgstr "" -#: netbox/dcim/filtersets.py:418 netbox/dcim/filtersets.py:899 -#: netbox/dcim/filtersets.py:1001 netbox/dcim/filtersets.py:2052 -#: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:382 -#: netbox/ipam/filtersets.py:494 netbox/ipam/filtersets.py:1004 -#: netbox/virtualization/filtersets.py:182 +#: dcim/filtersets.py:420 dcim/filtersets.py:927 dcim/filtersets.py:1054 +#: dcim/filtersets.py:2109 extras/filtersets.py:651 ipam/filtersets.py:382 +#: ipam/filtersets.py:494 ipam/filtersets.py:1004 +#: virtualization/filtersets.py:184 msgid "Role (slug)" msgstr "" -#: netbox/dcim/filtersets.py:448 netbox/dcim/filtersets.py:1070 -#: netbox/dcim/filtersets.py:1391 netbox/dcim/filtersets.py:1489 -#: netbox/dcim/filtersets.py:2444 +#: dcim/filtersets.py:450 dcim/filtersets.py:1123 dcim/filtersets.py:1444 +#: dcim/filtersets.py:1542 dcim/filtersets.py:2501 msgid "Rack (ID)" msgstr "" -#: netbox/dcim/filtersets.py:508 netbox/extras/filtersets.py:293 -#: netbox/extras/filtersets.py:337 netbox/extras/filtersets.py:359 -#: netbox/extras/filtersets.py:419 netbox/users/filtersets.py:113 -#: netbox/users/filtersets.py:180 +#: dcim/filtersets.py:510 extras/filtersets.py:298 extras/filtersets.py:350 +#: extras/filtersets.py:395 extras/filtersets.py:417 extras/filtersets.py:477 +#: users/filtersets.py:113 users/filtersets.py:180 msgid "User (name)" msgstr "" -#: netbox/dcim/filtersets.py:550 +#: dcim/filtersets.py:552 msgid "Default platform (ID)" msgstr "" -#: netbox/dcim/filtersets.py:556 +#: dcim/filtersets.py:558 msgid "Default platform (slug)" msgstr "" -#: netbox/dcim/filtersets.py:559 netbox/dcim/forms/filtersets.py:518 +#: dcim/filtersets.py:561 dcim/forms/filtersets.py:519 msgid "Has a front image" msgstr "" -#: netbox/dcim/filtersets.py:563 netbox/dcim/forms/filtersets.py:525 +#: dcim/filtersets.py:565 dcim/forms/filtersets.py:526 msgid "Has a rear image" msgstr "" -#: netbox/dcim/filtersets.py:568 netbox/dcim/filtersets.py:689 -#: netbox/dcim/filtersets.py:1139 netbox/dcim/forms/filtersets.py:532 -#: netbox/dcim/forms/filtersets.py:628 netbox/dcim/forms/filtersets.py:849 +#: dcim/filtersets.py:570 dcim/filtersets.py:717 dcim/filtersets.py:1192 +#: dcim/forms/filtersets.py:533 dcim/forms/filtersets.py:642 +#: dcim/forms/filtersets.py:868 msgid "Has console ports" msgstr "" -#: netbox/dcim/filtersets.py:572 netbox/dcim/filtersets.py:693 -#: netbox/dcim/filtersets.py:1143 netbox/dcim/forms/filtersets.py:539 -#: netbox/dcim/forms/filtersets.py:635 netbox/dcim/forms/filtersets.py:856 +#: dcim/filtersets.py:574 dcim/filtersets.py:721 dcim/filtersets.py:1196 +#: dcim/forms/filtersets.py:540 dcim/forms/filtersets.py:649 +#: dcim/forms/filtersets.py:875 msgid "Has console server ports" msgstr "" -#: netbox/dcim/filtersets.py:576 netbox/dcim/filtersets.py:697 -#: netbox/dcim/filtersets.py:1147 netbox/dcim/forms/filtersets.py:546 -#: netbox/dcim/forms/filtersets.py:642 netbox/dcim/forms/filtersets.py:863 +#: dcim/filtersets.py:578 dcim/filtersets.py:725 dcim/filtersets.py:1200 +#: dcim/forms/filtersets.py:547 dcim/forms/filtersets.py:656 +#: dcim/forms/filtersets.py:882 msgid "Has power ports" msgstr "" -#: netbox/dcim/filtersets.py:580 netbox/dcim/filtersets.py:701 -#: netbox/dcim/filtersets.py:1151 netbox/dcim/forms/filtersets.py:553 -#: netbox/dcim/forms/filtersets.py:649 netbox/dcim/forms/filtersets.py:870 +#: dcim/filtersets.py:582 dcim/filtersets.py:729 dcim/filtersets.py:1204 +#: dcim/forms/filtersets.py:554 dcim/forms/filtersets.py:663 +#: dcim/forms/filtersets.py:889 msgid "Has power outlets" msgstr "" -#: netbox/dcim/filtersets.py:584 netbox/dcim/filtersets.py:705 -#: netbox/dcim/filtersets.py:1155 netbox/dcim/forms/filtersets.py:560 -#: netbox/dcim/forms/filtersets.py:656 netbox/dcim/forms/filtersets.py:877 +#: dcim/filtersets.py:586 dcim/filtersets.py:733 dcim/filtersets.py:1208 +#: dcim/forms/filtersets.py:561 dcim/forms/filtersets.py:670 +#: dcim/forms/filtersets.py:896 msgid "Has interfaces" msgstr "" -#: netbox/dcim/filtersets.py:588 netbox/dcim/filtersets.py:709 -#: netbox/dcim/filtersets.py:1159 netbox/dcim/forms/filtersets.py:567 -#: netbox/dcim/forms/filtersets.py:663 netbox/dcim/forms/filtersets.py:884 +#: dcim/filtersets.py:590 dcim/filtersets.py:737 dcim/filtersets.py:1212 +#: dcim/forms/filtersets.py:568 dcim/forms/filtersets.py:677 +#: dcim/forms/filtersets.py:903 msgid "Has pass-through ports" msgstr "" -#: netbox/dcim/filtersets.py:592 netbox/dcim/filtersets.py:1163 -#: netbox/dcim/forms/filtersets.py:581 +#: dcim/filtersets.py:594 dcim/filtersets.py:1216 dcim/forms/filtersets.py:582 msgid "Has module bays" msgstr "" -#: netbox/dcim/filtersets.py:596 netbox/dcim/filtersets.py:1167 -#: netbox/dcim/forms/filtersets.py:574 +#: dcim/filtersets.py:598 dcim/filtersets.py:1220 dcim/forms/filtersets.py:575 msgid "Has device bays" msgstr "" -#: netbox/dcim/filtersets.py:600 netbox/dcim/forms/filtersets.py:588 +#: dcim/filtersets.py:602 dcim/forms/filtersets.py:589 msgid "Has inventory items" msgstr "" -#: netbox/dcim/filtersets.py:757 netbox/dcim/filtersets.py:990 -#: netbox/dcim/filtersets.py:1510 +#: dcim/filtersets.py:697 +msgid "Profile (ID)" +msgstr "" + +#: dcim/filtersets.py:703 +msgid "Profile (name)" +msgstr "" + +#: dcim/filtersets.py:785 dcim/filtersets.py:1041 dcim/filtersets.py:1563 msgid "Device type (ID)" msgstr "" -#: netbox/dcim/filtersets.py:773 netbox/dcim/filtersets.py:1326 +#: dcim/filtersets.py:801 dcim/filtersets.py:1379 msgid "Module type (ID)" msgstr "" -#: netbox/dcim/filtersets.py:805 netbox/dcim/filtersets.py:1665 +#: dcim/filtersets.py:833 dcim/filtersets.py:1718 msgid "Power port (ID)" msgstr "" -#: netbox/dcim/filtersets.py:879 netbox/dcim/filtersets.py:2032 +#: dcim/filtersets.py:907 dcim/filtersets.py:2089 msgid "Parent inventory item (ID)" msgstr "" -#: netbox/dcim/filtersets.py:922 netbox/dcim/filtersets.py:948 -#: netbox/dcim/filtersets.py:1135 netbox/virtualization/filtersets.py:204 +#: dcim/filtersets.py:950 dcim/filtersets.py:999 dcim/filtersets.py:1188 +#: virtualization/filtersets.py:206 msgid "Config template (ID)" msgstr "" -#: netbox/dcim/filtersets.py:986 +#: dcim/filtersets.py:954 dcim/filtersets.py:966 +msgid "Parent device role (ID)" +msgstr "" + +#: dcim/filtersets.py:960 dcim/filtersets.py:973 +msgid "Parent device role (slug)" +msgstr "" + +#: dcim/filtersets.py:1037 msgid "Device type (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1006 +#: dcim/filtersets.py:1059 msgid "Parent Device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1010 netbox/virtualization/filtersets.py:186 +#: dcim/filtersets.py:1063 virtualization/filtersets.py:188 msgid "Platform (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1016 netbox/extras/filtersets.py:569 -#: netbox/virtualization/filtersets.py:192 +#: dcim/filtersets.py:1069 extras/filtersets.py:662 +#: virtualization/filtersets.py:194 msgid "Platform (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1052 netbox/dcim/filtersets.py:1375 -#: netbox/dcim/filtersets.py:1473 netbox/dcim/filtersets.py:2134 -#: netbox/dcim/filtersets.py:2376 netbox/dcim/filtersets.py:2435 +#: dcim/filtersets.py:1105 dcim/filtersets.py:1428 dcim/filtersets.py:1526 +#: dcim/filtersets.py:2191 dcim/filtersets.py:2433 dcim/filtersets.py:2492 msgid "Site name (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1075 +#: dcim/filtersets.py:1128 msgid "Parent bay (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1079 +#: dcim/filtersets.py:1132 msgid "VM cluster (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1085 netbox/extras/filtersets.py:591 -#: netbox/virtualization/filtersets.py:102 +#: dcim/filtersets.py:1138 extras/filtersets.py:684 +#: virtualization/filtersets.py:102 msgid "Cluster group (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1090 netbox/virtualization/filtersets.py:96 +#: dcim/filtersets.py:1143 virtualization/filtersets.py:96 msgid "Cluster group (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1096 +#: dcim/filtersets.py:1149 msgid "Device model (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1107 netbox/dcim/forms/bulk_edit.py:525 +#: dcim/filtersets.py:1160 dcim/forms/bulk_edit.py:539 msgid "Is full depth" msgstr "" -#: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 -#: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 -#: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 -#: netbox/virtualization/filtersets.py:196 -#: netbox/virtualization/filtersets.py:268 -#: netbox/virtualization/forms/filtersets.py:178 -#: netbox/virtualization/forms/filtersets.py:231 +#: dcim/filtersets.py:1164 dcim/forms/filtersets.py:838 +#: dcim/forms/filtersets.py:1463 dcim/forms/filtersets.py:1669 +#: dcim/forms/filtersets.py:1674 dcim/forms/model_forms.py:1887 +#: dcim/models/devices.py:1234 dcim/models/devices.py:1254 +#: virtualization/filtersets.py:198 virtualization/filtersets.py:270 +#: virtualization/forms/filtersets.py:178 +#: virtualization/forms/filtersets.py:231 msgid "MAC address" msgstr "" -#: netbox/dcim/filtersets.py:1118 netbox/dcim/filtersets.py:1283 -#: netbox/dcim/forms/filtersets.py:828 netbox/dcim/forms/filtersets.py:931 -#: netbox/virtualization/filtersets.py:200 -#: netbox/virtualization/forms/filtersets.py:182 +#: dcim/filtersets.py:1171 dcim/filtersets.py:1336 dcim/forms/filtersets.py:847 +#: dcim/forms/filtersets.py:950 virtualization/filtersets.py:202 +#: virtualization/forms/filtersets.py:182 msgid "Has a primary IP" msgstr "" -#: netbox/dcim/filtersets.py:1122 +#: dcim/filtersets.py:1175 msgid "Has an out-of-band IP" msgstr "" -#: netbox/dcim/filtersets.py:1127 +#: dcim/filtersets.py:1180 msgid "Virtual chassis (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1131 +#: dcim/filtersets.py:1184 msgid "Is a virtual chassis member" msgstr "" -#: netbox/dcim/filtersets.py:1172 +#: dcim/filtersets.py:1225 msgid "OOB IP (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1176 +#: dcim/filtersets.py:1229 msgid "Has virtual device context" msgstr "" -#: netbox/dcim/filtersets.py:1266 +#: dcim/filtersets.py:1319 msgid "VDC (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1271 +#: dcim/filtersets.py:1324 msgid "Device model" msgstr "" -#: netbox/dcim/filtersets.py:1332 +#: dcim/filtersets.py:1385 msgid "Module type (model)" msgstr "" -#: netbox/dcim/filtersets.py:1338 +#: dcim/filtersets.py:1391 msgid "Module bay (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1397 netbox/dcim/filtersets.py:1495 +#: dcim/filtersets.py:1450 dcim/filtersets.py:1548 msgid "Rack (name)" msgstr "" -#: netbox/dcim/filtersets.py:1401 netbox/dcim/filtersets.py:1499 -#: netbox/dcim/filtersets.py:1685 netbox/ipam/filtersets.py:606 -#: netbox/ipam/filtersets.py:846 netbox/ipam/filtersets.py:1168 -#: netbox/virtualization/filtersets.py:127 netbox/vpn/filtersets.py:379 +#: dcim/filtersets.py:1454 dcim/filtersets.py:1552 dcim/filtersets.py:1742 +#: ipam/filtersets.py:606 ipam/filtersets.py:846 ipam/filtersets.py:1174 +#: virtualization/filtersets.py:127 vpn/filtersets.py:382 msgid "Device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1407 netbox/dcim/filtersets.py:1505 -#: netbox/dcim/filtersets.py:1680 netbox/ipam/filtersets.py:601 -#: netbox/ipam/filtersets.py:841 netbox/ipam/filtersets.py:1174 -#: netbox/vpn/filtersets.py:374 +#: dcim/filtersets.py:1460 dcim/filtersets.py:1558 dcim/filtersets.py:1737 +#: ipam/filtersets.py:601 ipam/filtersets.py:841 ipam/filtersets.py:1169 +#: vpn/filtersets.py:377 msgid "Device (name)" msgstr "" -#: netbox/dcim/filtersets.py:1516 +#: dcim/filtersets.py:1569 msgid "Device type (model)" msgstr "" -#: netbox/dcim/filtersets.py:1521 +#: dcim/filtersets.py:1574 msgid "Device role (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1527 +#: dcim/filtersets.py:1580 msgid "Device role (slug)" msgstr "" -#: netbox/dcim/filtersets.py:1532 +#: dcim/filtersets.py:1585 msgid "Virtual Chassis (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1538 netbox/dcim/forms/filtersets.py:110 -#: netbox/dcim/tables/devices.py:216 netbox/netbox/navigation/menu.py:79 -#: netbox/templates/dcim/device.html:120 -#: netbox/templates/dcim/device_edit.html:95 -#: netbox/templates/dcim/virtualchassis.html:20 -#: netbox/templates/dcim/virtualchassis_add.html:12 -#: netbox/templates/dcim/virtualchassis_edit.html:28 +#: dcim/filtersets.py:1591 dcim/forms/filtersets.py:111 +#: dcim/tables/devices.py:216 netbox/navigation/menu.py:79 +#: templates/dcim/device.html:120 templates/dcim/device_edit.html:95 +#: templates/dcim/virtualchassis.html:20 +#: templates/dcim/virtualchassis_add.html:12 +#: templates/dcim/virtualchassis_edit.html:28 msgid "Virtual Chassis" msgstr "" -#: netbox/dcim/filtersets.py:1562 +#: dcim/filtersets.py:1615 msgid "Module (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1569 +#: dcim/filtersets.py:1622 msgid "Cable (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1690 netbox/ipam/filtersets.py:611 -#: netbox/ipam/filtersets.py:851 netbox/ipam/filtersets.py:1184 -#: netbox/vpn/filtersets.py:385 +#: dcim/filtersets.py:1747 ipam/filtersets.py:611 ipam/filtersets.py:851 +#: ipam/filtersets.py:1179 vpn/filtersets.py:388 msgid "Virtual machine (name)" msgstr "" -#: netbox/dcim/filtersets.py:1695 netbox/ipam/filtersets.py:616 -#: netbox/ipam/filtersets.py:856 netbox/ipam/filtersets.py:1178 -#: netbox/virtualization/filtersets.py:248 -#: netbox/virtualization/filtersets.py:299 netbox/vpn/filtersets.py:390 +#: dcim/filtersets.py:1752 ipam/filtersets.py:616 ipam/filtersets.py:856 +#: ipam/filtersets.py:1184 virtualization/filtersets.py:250 +#: virtualization/filtersets.py:301 vpn/filtersets.py:393 msgid "Virtual machine (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1701 netbox/ipam/filtersets.py:622 -#: netbox/vpn/filtersets.py:97 netbox/vpn/filtersets.py:396 +#: dcim/filtersets.py:1758 ipam/filtersets.py:622 vpn/filtersets.py:97 +#: vpn/filtersets.py:399 msgid "Interface (name)" msgstr "" -#: netbox/dcim/filtersets.py:1712 netbox/ipam/filtersets.py:633 -#: netbox/vpn/filtersets.py:108 netbox/vpn/filtersets.py:407 +#: dcim/filtersets.py:1769 ipam/filtersets.py:633 vpn/filtersets.py:108 +#: vpn/filtersets.py:410 msgid "VM interface (name)" msgstr "" -#: netbox/dcim/filtersets.py:1717 netbox/ipam/filtersets.py:638 -#: netbox/vpn/filtersets.py:113 +#: dcim/filtersets.py:1774 ipam/filtersets.py:638 vpn/filtersets.py:113 msgid "VM interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1759 netbox/templates/dcim/interface.html:81 -#: netbox/templates/virtualization/vminterface.html:55 -#: netbox/virtualization/forms/model_forms.py:395 +#: dcim/filtersets.py:1816 templates/dcim/interface.html:81 +#: templates/virtualization/vminterface.html:55 +#: virtualization/forms/model_forms.py:395 msgid "802.1Q Mode" msgstr "" -#: netbox/dcim/filtersets.py:1763 netbox/ipam/forms/bulk_import.py:192 -#: netbox/vpn/forms/bulk_import.py:308 +#: dcim/filtersets.py:1820 ipam/forms/bulk_import.py:192 +#: vpn/forms/bulk_import.py:313 msgid "Assigned VLAN" msgstr "" -#: netbox/dcim/filtersets.py:1767 +#: dcim/filtersets.py:1824 msgid "Assigned VID" msgstr "" -#: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 -#: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1442 -#: netbox/dcim/models/device_components.py:752 -#: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 -#: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 -#: netbox/ipam/filtersets.py:579 netbox/ipam/filtersets.py:590 -#: netbox/ipam/forms/bulk_edit.py:226 netbox/ipam/forms/bulk_edit.py:282 -#: netbox/ipam/forms/bulk_edit.py:324 netbox/ipam/forms/bulk_import.py:160 -#: netbox/ipam/forms/bulk_import.py:249 netbox/ipam/forms/bulk_import.py:285 -#: netbox/ipam/forms/filtersets.py:69 netbox/ipam/forms/filtersets.py:180 -#: netbox/ipam/forms/filtersets.py:325 netbox/ipam/forms/model_forms.py:65 -#: netbox/ipam/forms/model_forms.py:208 netbox/ipam/forms/model_forms.py:256 -#: netbox/ipam/forms/model_forms.py:310 netbox/ipam/forms/model_forms.py:474 -#: netbox/ipam/forms/model_forms.py:488 netbox/ipam/forms/model_forms.py:502 -#: netbox/ipam/models/ip.py:217 netbox/ipam/models/ip.py:498 -#: netbox/ipam/models/ip.py:719 netbox/ipam/models/vrfs.py:61 -#: netbox/ipam/tables/ip.py:188 netbox/ipam/tables/ip.py:261 -#: netbox/ipam/tables/ip.py:312 netbox/ipam/tables/ip.py:402 -#: netbox/templates/dcim/interface.html:152 -#: netbox/templates/ipam/ipaddress.html:18 -#: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 -#: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:90 -#: netbox/virtualization/forms/bulk_edit.py:243 -#: netbox/virtualization/forms/bulk_import.py:177 -#: netbox/virtualization/forms/filtersets.py:236 -#: netbox/virtualization/forms/model_forms.py:368 -#: netbox/virtualization/models/virtualmachines.py:331 -#: netbox/virtualization/tables/virtualmachines.py:113 +#: dcim/filtersets.py:1829 dcim/forms/bulk_edit.py:1591 +#: dcim/forms/bulk_import.py:952 dcim/forms/filtersets.py:1516 +#: dcim/forms/model_forms.py:1536 dcim/models/device_components.py:761 +#: dcim/tables/devices.py:654 ipam/filtersets.py:335 ipam/filtersets.py:346 +#: ipam/filtersets.py:478 ipam/filtersets.py:579 ipam/filtersets.py:590 +#: ipam/forms/bulk_edit.py:226 ipam/forms/bulk_edit.py:282 +#: ipam/forms/bulk_edit.py:329 ipam/forms/bulk_import.py:160 +#: ipam/forms/bulk_import.py:249 ipam/forms/bulk_import.py:285 +#: ipam/forms/filtersets.py:69 ipam/forms/filtersets.py:180 +#: ipam/forms/filtersets.py:332 ipam/forms/model_forms.py:65 +#: ipam/forms/model_forms.py:208 ipam/forms/model_forms.py:256 +#: ipam/forms/model_forms.py:310 ipam/forms/model_forms.py:474 +#: ipam/forms/model_forms.py:488 ipam/forms/model_forms.py:502 +#: ipam/models/ip.py:217 ipam/models/ip.py:501 ipam/models/ip.py:730 +#: ipam/models/vrfs.py:61 ipam/tables/ip.py:189 ipam/tables/ip.py:262 +#: ipam/tables/ip.py:318 ipam/tables/ip.py:418 +#: templates/dcim/interface.html:152 templates/ipam/ipaddress.html:18 +#: templates/ipam/iprange.html:47 templates/ipam/prefix.html:19 +#: templates/ipam/vrf.html:7 templates/ipam/vrf.html:13 +#: templates/virtualization/vminterface.html:90 +#: virtualization/forms/bulk_edit.py:243 +#: virtualization/forms/bulk_import.py:177 +#: virtualization/forms/filtersets.py:236 +#: virtualization/forms/model_forms.py:368 +#: virtualization/models/virtualmachines.py:336 +#: virtualization/tables/virtualmachines.py:113 msgid "VRF" msgstr "" -#: netbox/dcim/filtersets.py:1778 netbox/ipam/filtersets.py:341 -#: netbox/ipam/filtersets.py:352 netbox/ipam/filtersets.py:484 -#: netbox/ipam/filtersets.py:585 netbox/ipam/filtersets.py:596 +#: dcim/filtersets.py:1835 ipam/filtersets.py:341 ipam/filtersets.py:352 +#: ipam/filtersets.py:484 ipam/filtersets.py:585 ipam/filtersets.py:596 msgid "VRF (RD)" msgstr "" -#: netbox/dcim/filtersets.py:1783 netbox/ipam/filtersets.py:1036 -#: netbox/vpn/filtersets.py:342 +#: dcim/filtersets.py:1840 ipam/filtersets.py:1036 vpn/filtersets.py:345 msgid "L2VPN (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1789 netbox/dcim/forms/filtersets.py:1497 -#: netbox/dcim/tables/devices.py:583 netbox/ipam/filtersets.py:1042 -#: netbox/ipam/forms/filtersets.py:584 netbox/ipam/tables/vlans.py:113 -#: netbox/templates/dcim/interface.html:99 netbox/templates/ipam/vlan.html:82 -#: netbox/templates/vpn/l2vpntermination.html:12 -#: netbox/virtualization/forms/filtersets.py:241 -#: netbox/vpn/forms/bulk_import.py:280 netbox/vpn/forms/filtersets.py:252 -#: netbox/vpn/forms/model_forms.py:412 netbox/vpn/forms/model_forms.py:430 -#: netbox/vpn/models/l2vpn.py:63 netbox/vpn/tables/l2vpn.py:55 +#: dcim/filtersets.py:1846 dcim/forms/filtersets.py:1521 +#: dcim/tables/devices.py:590 ipam/filtersets.py:1042 +#: ipam/forms/filtersets.py:592 ipam/tables/vlans.py:115 +#: templates/dcim/interface.html:99 templates/ipam/vlan.html:82 +#: templates/vpn/l2vpntermination.html:12 +#: virtualization/forms/filtersets.py:241 vpn/forms/bulk_import.py:285 +#: vpn/forms/filtersets.py:257 vpn/forms/model_forms.py:412 +#: vpn/forms/model_forms.py:430 vpn/models/l2vpn.py:68 vpn/tables/l2vpn.py:58 msgid "L2VPN" msgstr "" -#: netbox/dcim/filtersets.py:1794 netbox/ipam/filtersets.py:1117 +#: dcim/filtersets.py:1851 ipam/filtersets.py:1117 msgid "VLAN Translation Policy (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1459 -#: netbox/dcim/models/device_components.py:571 -#: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 -#: netbox/templates/ipam/vlantranslationpolicy.html:11 -#: netbox/virtualization/forms/bulk_edit.py:248 -#: netbox/virtualization/forms/filtersets.py:251 -#: netbox/virtualization/forms/model_forms.py:373 +#: dcim/filtersets.py:1857 dcim/forms/filtersets.py:1487 +#: dcim/forms/model_forms.py:1553 dcim/models/device_components.py:580 +#: ipam/forms/filtersets.py:511 ipam/forms/model_forms.py:712 +#: templates/ipam/vlantranslationpolicy.html:11 +#: virtualization/forms/bulk_edit.py:248 virtualization/forms/filtersets.py:251 +#: virtualization/forms/model_forms.py:373 msgid "VLAN Translation Policy" msgstr "" -#: netbox/dcim/filtersets.py:1834 +#: dcim/filtersets.py:1891 msgid "Virtual Chassis Interfaces for Device" msgstr "" -#: netbox/dcim/filtersets.py:1839 +#: dcim/filtersets.py:1896 msgid "Virtual Chassis Interfaces for Device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1843 +#: dcim/filtersets.py:1900 msgid "Kind of interface" msgstr "" -#: netbox/dcim/filtersets.py:1848 netbox/virtualization/filtersets.py:259 +#: dcim/filtersets.py:1905 virtualization/filtersets.py:261 msgid "Parent interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1853 netbox/virtualization/filtersets.py:264 +#: dcim/filtersets.py:1910 virtualization/filtersets.py:266 msgid "Bridged interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1858 +#: dcim/filtersets.py:1915 msgid "LAG interface (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1866 netbox/dcim/tables/devices.py:605 -#: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 -#: netbox/templates/dcim/macaddress.html:11 -#: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:79 +#: dcim/filtersets.py:1923 dcim/tables/devices.py:612 +#: dcim/tables/devices.py:1142 templates/dcim/interface.html:131 +#: templates/dcim/macaddress.html:11 templates/dcim/macaddress.html:14 +#: templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "" -#: netbox/dcim/filtersets.py:1871 netbox/virtualization/filtersets.py:273 +#: dcim/filtersets.py:1928 virtualization/filtersets.py:275 msgid "Primary MAC address (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 -#: netbox/virtualization/filtersets.py:279 -#: netbox/virtualization/forms/model_forms.py:311 +#: dcim/filtersets.py:1934 dcim/forms/model_forms.py:1540 +#: virtualization/filtersets.py:281 virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "" -#: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 -#: netbox/templates/dcim/virtualdevicecontext.html:15 +#: dcim/filtersets.py:1956 dcim/filtersets.py:1968 +#: dcim/forms/filtersets.py:1423 dcim/forms/model_forms.py:1867 +#: templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "" -#: netbox/dcim/filtersets.py:1905 +#: dcim/filtersets.py:1962 msgid "Virtual Device Context (Identifier)" msgstr "" -#: netbox/dcim/filtersets.py:1916 netbox/templates/wireless/wirelesslan.html:11 -#: netbox/wireless/forms/model_forms.py:55 +#: dcim/filtersets.py:1973 templates/wireless/wirelesslan.html:11 +#: wireless/forms/model_forms.py:56 msgid "Wireless LAN" msgstr "" -#: netbox/dcim/filtersets.py:1920 netbox/dcim/tables/devices.py:634 +#: dcim/filtersets.py:1977 dcim/tables/devices.py:641 msgid "Wireless link" msgstr "" -#: netbox/dcim/filtersets.py:1930 +#: dcim/filtersets.py:1987 msgid "Virtual circuit termination (ID)" msgstr "" -#: netbox/dcim/filtersets.py:1999 +#: dcim/filtersets.py:2056 msgid "Parent module bay (ID)" msgstr "" -#: netbox/dcim/filtersets.py:2004 +#: dcim/filtersets.py:2061 msgid "Installed module (ID)" msgstr "" -#: netbox/dcim/filtersets.py:2015 +#: dcim/filtersets.py:2072 msgid "Installed device (ID)" msgstr "" -#: netbox/dcim/filtersets.py:2021 +#: dcim/filtersets.py:2078 msgid "Installed device (name)" msgstr "" -#: netbox/dcim/filtersets.py:2091 +#: dcim/filtersets.py:2148 msgid "Master (ID)" msgstr "" -#: netbox/dcim/filtersets.py:2097 +#: dcim/filtersets.py:2154 msgid "Master (name)" msgstr "" -#: netbox/dcim/filtersets.py:2139 netbox/tenancy/filtersets.py:245 +#: dcim/filtersets.py:2196 tenancy/filtersets.py:250 msgid "Tenant (ID)" msgstr "" -#: netbox/dcim/filtersets.py:2145 netbox/extras/filtersets.py:618 -#: netbox/tenancy/filtersets.py:251 +#: dcim/filtersets.py:2202 extras/filtersets.py:711 tenancy/filtersets.py:256 msgid "Tenant (slug)" msgstr "" -#: netbox/dcim/filtersets.py:2181 netbox/dcim/forms/filtersets.py:1126 +#: dcim/filtersets.py:2238 dcim/forms/filtersets.py:1145 msgid "Unterminated" msgstr "" -#: netbox/dcim/filtersets.py:2439 +#: dcim/filtersets.py:2496 msgid "Power panel (ID)" msgstr "" -#: netbox/dcim/forms/bulk_create.py:40 netbox/extras/forms/filtersets.py:408 -#: netbox/extras/forms/model_forms.py:581 -#: netbox/extras/forms/model_forms.py:633 netbox/netbox/forms/base.py:86 -#: netbox/netbox/forms/mixins.py:91 netbox/netbox/tables/columns.py:481 -#: netbox/templates/circuits/inc/circuit_termination.html:32 -#: netbox/templates/generic/bulk_edit.html:65 -#: netbox/templates/inc/panels/tags.html:5 -#: netbox/utilities/forms/fields/fields.py:81 +#: dcim/forms/bulk_create.py:40 extras/forms/filtersets.py:443 +#: extras/forms/model_forms.py:649 extras/forms/model_forms.py:701 +#: netbox/forms/base.py:86 netbox/forms/mixins.py:91 +#: netbox/tables/columns.py:486 +#: templates/circuits/inc/circuit_termination.html:32 +#: templates/generic/bulk_edit.html:65 templates/inc/panels/tags.html:5 +#: utilities/forms/fields/fields.py:81 msgid "Tags" msgstr "" -#: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 -#: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:208 -#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 -#: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 -#: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 -#: netbox/templates/dcim/modulebay.html:38 -#: netbox/templates/dcim/virtualchassis.html:66 -#: netbox/templates/dcim/virtualchassis_edit.html:59 +#: dcim/forms/bulk_create.py:112 dcim/forms/filtersets.py:1586 +#: dcim/forms/model_forms.py:592 dcim/forms/model_forms.py:651 +#: dcim/forms/object_create.py:208 dcim/forms/object_create.py:357 +#: dcim/tables/devices.py:175 dcim/tables/devices.py:747 +#: dcim/tables/devicetypes.py:253 templates/dcim/device.html:43 +#: templates/dcim/device.html:131 templates/dcim/modulebay.html:38 +#: templates/dcim/virtualchassis.html:66 +#: templates/dcim/virtualchassis_edit.html:59 msgid "Position" msgstr "" -#: netbox/dcim/forms/bulk_create.py:114 +#: dcim/forms/bulk_create.py:114 msgid "" "Alphanumeric ranges are supported. (Must match the number of names being " "created.)" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:136 +#: dcim/forms/bulk_edit.py:141 msgid "Contact name" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:141 +#: dcim/forms/bulk_edit.py:146 msgid "Contact phone" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:147 +#: dcim/forms/bulk_edit.py:152 msgid "Contact E-mail" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:150 netbox/dcim/forms/bulk_import.py:125 -#: netbox/dcim/forms/model_forms.py:132 +#: dcim/forms/bulk_edit.py:155 dcim/forms/bulk_import.py:126 +#: dcim/forms/model_forms.py:137 msgid "Time zone" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:228 netbox/dcim/forms/bulk_edit.py:504 -#: netbox/dcim/forms/bulk_edit.py:568 netbox/dcim/forms/bulk_edit.py:641 -#: netbox/dcim/forms/bulk_edit.py:665 netbox/dcim/forms/bulk_edit.py:758 -#: netbox/dcim/forms/bulk_edit.py:1285 netbox/dcim/forms/bulk_edit.py:1718 -#: netbox/dcim/forms/bulk_import.py:184 netbox/dcim/forms/bulk_import.py:395 -#: netbox/dcim/forms/bulk_import.py:429 netbox/dcim/forms/bulk_import.py:477 -#: netbox/dcim/forms/bulk_import.py:513 netbox/dcim/forms/bulk_import.py:1112 -#: netbox/dcim/forms/filtersets.py:314 netbox/dcim/forms/filtersets.py:373 -#: netbox/dcim/forms/filtersets.py:495 netbox/dcim/forms/filtersets.py:620 -#: netbox/dcim/forms/filtersets.py:701 netbox/dcim/forms/filtersets.py:783 -#: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 -#: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 -#: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 -#: netbox/dcim/forms/model_forms.py:1595 netbox/dcim/forms/object_import.py:188 -#: netbox/dcim/tables/devices.py:107 netbox/dcim/tables/devices.py:182 -#: netbox/dcim/tables/devices.py:969 netbox/dcim/tables/devicetypes.py:85 -#: netbox/dcim/tables/devicetypes.py:315 netbox/dcim/tables/modules.py:20 -#: netbox/dcim/tables/modules.py:61 netbox/dcim/tables/racks.py:58 -#: netbox/dcim/tables/racks.py:131 netbox/templates/dcim/devicetype.html:14 -#: netbox/templates/dcim/inventoryitem.html:48 -#: netbox/templates/dcim/manufacturer.html:33 -#: netbox/templates/dcim/modulebay.html:62 -#: netbox/templates/dcim/moduletype.html:27 -#: netbox/templates/dcim/platform.html:37 -#: netbox/templates/dcim/racktype.html:16 +#: dcim/forms/bulk_edit.py:234 dcim/forms/bulk_edit.py:518 +#: dcim/forms/bulk_edit.py:606 dcim/forms/bulk_edit.py:685 +#: dcim/forms/bulk_edit.py:709 dcim/forms/bulk_edit.py:802 +#: dcim/forms/bulk_edit.py:1329 dcim/forms/bulk_edit.py:1765 +#: dcim/forms/bulk_import.py:188 dcim/forms/bulk_import.py:399 +#: dcim/forms/bulk_import.py:448 dcim/forms/bulk_import.py:508 +#: dcim/forms/bulk_import.py:544 dcim/forms/bulk_import.py:1143 +#: dcim/forms/filtersets.py:315 dcim/forms/filtersets.py:374 +#: dcim/forms/filtersets.py:496 dcim/forms/filtersets.py:634 +#: dcim/forms/filtersets.py:720 dcim/forms/filtersets.py:802 +#: dcim/forms/filtersets.py:1015 dcim/forms/filtersets.py:1627 +#: dcim/forms/model_forms.py:218 dcim/forms/model_forms.py:353 +#: dcim/forms/model_forms.py:365 dcim/forms/model_forms.py:437 +#: dcim/forms/model_forms.py:539 dcim/forms/model_forms.py:1220 +#: dcim/forms/model_forms.py:1689 dcim/forms/object_import.py:188 +#: dcim/tables/devices.py:107 dcim/tables/devices.py:182 +#: dcim/tables/devices.py:976 dcim/tables/devicetypes.py:85 +#: dcim/tables/devicetypes.py:315 dcim/tables/modules.py:49 +#: dcim/tables/modules.py:95 dcim/tables/racks.py:58 dcim/tables/racks.py:135 +#: templates/dcim/devicetype.html:14 templates/dcim/inventoryitem.html:48 +#: templates/dcim/manufacturer.html:33 templates/dcim/module.html:95 +#: templates/dcim/modulebay.html:62 templates/dcim/moduletype.html:31 +#: templates/dcim/platform.html:37 templates/dcim/racktype.html:16 msgid "Manufacturer" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:233 netbox/dcim/forms/bulk_edit.py:381 -#: netbox/dcim/forms/bulk_import.py:193 netbox/dcim/forms/bulk_import.py:272 -#: netbox/dcim/forms/filtersets.py:256 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:6 +#: dcim/forms/bulk_edit.py:239 dcim/forms/bulk_edit.py:392 +#: dcim/forms/bulk_import.py:197 dcim/forms/bulk_import.py:276 +#: dcim/forms/filtersets.py:257 +#: templates/dcim/inc/panels/racktype_dimensions.html:6 msgid "Form factor" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:238 netbox/dcim/forms/bulk_edit.py:386 -#: netbox/dcim/forms/bulk_import.py:201 netbox/dcim/forms/bulk_import.py:275 -#: netbox/dcim/forms/filtersets.py:261 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:10 +#: dcim/forms/bulk_edit.py:244 dcim/forms/bulk_edit.py:397 +#: dcim/forms/bulk_import.py:205 dcim/forms/bulk_import.py:279 +#: dcim/forms/filtersets.py:262 +#: templates/dcim/inc/panels/racktype_dimensions.html:10 msgid "Width" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:244 netbox/dcim/forms/bulk_edit.py:392 -#: netbox/dcim/forms/bulk_import.py:282 -#: netbox/templates/dcim/devicetype.html:37 +#: dcim/forms/bulk_edit.py:250 dcim/forms/bulk_edit.py:403 +#: dcim/forms/bulk_import.py:286 templates/dcim/devicetype.html:37 msgid "Height (U)" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:253 netbox/dcim/forms/bulk_edit.py:397 -#: netbox/dcim/forms/filtersets.py:275 +#: dcim/forms/bulk_edit.py:259 dcim/forms/bulk_edit.py:408 +#: dcim/forms/filtersets.py:276 msgid "Descending units" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:256 netbox/dcim/forms/bulk_edit.py:400 +#: dcim/forms/bulk_edit.py:262 dcim/forms/bulk_edit.py:411 msgid "Outer width" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:261 netbox/dcim/forms/bulk_edit.py:405 +#: dcim/forms/bulk_edit.py:267 dcim/forms/bulk_edit.py:416 +msgid "Outer height" +msgstr "" + +#: dcim/forms/bulk_edit.py:272 dcim/forms/bulk_edit.py:421 msgid "Outer depth" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:266 netbox/dcim/forms/bulk_edit.py:410 -#: netbox/dcim/forms/bulk_import.py:206 netbox/dcim/forms/bulk_import.py:285 +#: dcim/forms/bulk_edit.py:277 dcim/forms/bulk_edit.py:426 +#: dcim/forms/bulk_import.py:210 dcim/forms/bulk_import.py:289 msgid "Outer unit" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:271 netbox/dcim/forms/bulk_edit.py:415 +#: dcim/forms/bulk_edit.py:282 dcim/forms/bulk_edit.py:431 msgid "Mounting depth" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:276 netbox/dcim/forms/bulk_edit.py:303 -#: netbox/dcim/forms/bulk_edit.py:425 netbox/dcim/forms/bulk_edit.py:455 -#: netbox/dcim/forms/bulk_edit.py:538 netbox/dcim/forms/bulk_edit.py:561 -#: netbox/dcim/forms/bulk_edit.py:582 netbox/dcim/forms/bulk_edit.py:604 -#: netbox/dcim/forms/bulk_import.py:408 netbox/dcim/forms/bulk_import.py:440 -#: netbox/dcim/forms/filtersets.py:286 netbox/dcim/forms/filtersets.py:308 -#: netbox/dcim/forms/filtersets.py:328 netbox/dcim/forms/filtersets.py:402 -#: netbox/dcim/forms/filtersets.py:489 netbox/dcim/forms/filtersets.py:595 -#: netbox/dcim/forms/filtersets.py:614 netbox/dcim/forms/filtersets.py:675 -#: netbox/dcim/forms/model_forms.py:226 netbox/dcim/forms/model_forms.py:306 -#: netbox/dcim/tables/devicetypes.py:111 netbox/dcim/tables/modules.py:35 -#: netbox/dcim/tables/racks.py:74 netbox/dcim/tables/racks.py:171 -#: netbox/extras/forms/bulk_edit.py:53 netbox/extras/forms/bulk_edit.py:133 -#: netbox/extras/forms/bulk_edit.py:183 netbox/extras/forms/bulk_edit.py:288 -#: netbox/extras/forms/filtersets.py:65 netbox/extras/forms/filtersets.py:159 -#: netbox/extras/forms/filtersets.py:249 netbox/ipam/forms/bulk_edit.py:193 -#: netbox/templates/dcim/device.html:324 -#: netbox/templates/dcim/devicetype.html:49 -#: netbox/templates/dcim/moduletype.html:47 netbox/templates/dcim/rack.html:81 -#: netbox/templates/dcim/racktype.html:41 -#: netbox/templates/extras/configcontext.html:17 -#: netbox/templates/extras/customlink.html:25 -#: netbox/templates/extras/savedfilter.html:33 -#: netbox/templates/ipam/role.html:30 +#: dcim/forms/bulk_edit.py:287 dcim/forms/bulk_edit.py:314 +#: dcim/forms/bulk_edit.py:441 dcim/forms/bulk_edit.py:469 +#: dcim/forms/bulk_edit.py:552 dcim/forms/bulk_edit.py:575 +#: dcim/forms/bulk_edit.py:620 dcim/forms/bulk_edit.py:642 +#: dcim/forms/bulk_import.py:412 dcim/forms/bulk_import.py:459 +#: dcim/forms/filtersets.py:287 dcim/forms/filtersets.py:309 +#: dcim/forms/filtersets.py:329 dcim/forms/filtersets.py:403 +#: dcim/forms/filtersets.py:490 dcim/forms/filtersets.py:596 +#: dcim/forms/filtersets.py:623 dcim/forms/filtersets.py:689 +#: dcim/forms/model_forms.py:233 dcim/forms/model_forms.py:314 +#: dcim/tables/devicetypes.py:111 dcim/tables/modules.py:57 +#: dcim/tables/racks.py:78 dcim/tables/racks.py:179 +#: extras/forms/bulk_edit.py:54 extras/forms/bulk_edit.py:134 +#: extras/forms/bulk_edit.py:188 extras/forms/bulk_edit.py:216 +#: extras/forms/bulk_edit.py:312 extras/forms/bulk_edit.py:325 +#: extras/forms/bulk_import.py:238 extras/forms/filtersets.py:66 +#: extras/forms/filtersets.py:160 extras/forms/filtersets.py:254 +#: extras/forms/filtersets.py:284 extras/forms/model_forms.py:572 +#: ipam/forms/bulk_edit.py:193 templates/dcim/device.html:324 +#: templates/dcim/devicetype.html:49 templates/dcim/moduletype.html:51 +#: templates/dcim/rack.html:81 templates/dcim/racktype.html:41 +#: templates/extras/configcontext.html:17 templates/extras/customlink.html:25 +#: templates/extras/savedfilter.html:33 templates/extras/tableconfig.html:41 +#: templates/extras/tag.html:32 templates/ipam/role.html:30 msgid "Weight" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:281 netbox/dcim/forms/bulk_edit.py:430 -#: netbox/dcim/forms/filtersets.py:291 +#: dcim/forms/bulk_edit.py:292 dcim/forms/bulk_edit.py:446 +#: dcim/forms/filtersets.py:292 msgid "Max weight" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:286 netbox/dcim/forms/bulk_edit.py:435 -#: netbox/dcim/forms/bulk_edit.py:543 netbox/dcim/forms/bulk_edit.py:587 -#: netbox/dcim/forms/bulk_import.py:212 netbox/dcim/forms/bulk_import.py:297 -#: netbox/dcim/forms/bulk_import.py:413 netbox/dcim/forms/bulk_import.py:445 -#: netbox/dcim/forms/filtersets.py:296 netbox/dcim/forms/filtersets.py:599 -#: netbox/dcim/forms/filtersets.py:679 +#: dcim/forms/bulk_edit.py:297 dcim/forms/bulk_edit.py:451 +#: dcim/forms/bulk_edit.py:557 dcim/forms/bulk_edit.py:625 +#: dcim/forms/bulk_import.py:216 dcim/forms/bulk_import.py:301 +#: dcim/forms/bulk_import.py:417 dcim/forms/bulk_import.py:464 +#: dcim/forms/filtersets.py:297 dcim/forms/filtersets.py:600 +#: dcim/forms/filtersets.py:693 msgid "Weight unit" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:300 netbox/dcim/forms/filtersets.py:306 -#: netbox/dcim/forms/model_forms.py:222 netbox/dcim/forms/model_forms.py:261 -#: netbox/templates/dcim/rack.html:45 netbox/templates/dcim/racktype.html:13 +#: dcim/forms/bulk_edit.py:311 dcim/forms/filtersets.py:307 +#: dcim/forms/model_forms.py:229 dcim/forms/model_forms.py:268 +#: templates/dcim/rack.html:45 templates/dcim/racktype.html:13 msgid "Rack Type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:302 netbox/dcim/forms/model_forms.py:225 -#: netbox/dcim/forms/model_forms.py:305 +#: dcim/forms/bulk_edit.py:313 dcim/forms/bulk_edit.py:467 +#: dcim/forms/model_forms.py:232 dcim/forms/model_forms.py:313 msgid "Outer Dimensions" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:305 netbox/dcim/forms/model_forms.py:227 -#: netbox/dcim/forms/model_forms.py:307 netbox/templates/dcim/device.html:315 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:3 +#: dcim/forms/bulk_edit.py:316 dcim/forms/model_forms.py:234 +#: dcim/forms/model_forms.py:315 templates/dcim/device.html:315 +#: templates/dcim/inc/panels/racktype_dimensions.html:3 msgid "Dimensions" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:307 netbox/dcim/forms/filtersets.py:307 -#: netbox/dcim/forms/filtersets.py:327 netbox/dcim/forms/model_forms.py:229 -#: netbox/templates/dcim/inc/panels/racktype_numbering.html:3 +#: dcim/forms/bulk_edit.py:318 dcim/forms/filtersets.py:308 +#: dcim/forms/filtersets.py:328 dcim/forms/model_forms.py:236 +#: templates/dcim/inc/panels/racktype_numbering.html:3 msgid "Numbering" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:366 netbox/dcim/forms/bulk_import.py:262 -#: netbox/dcim/forms/filtersets.py:381 +#: dcim/forms/bulk_edit.py:377 dcim/forms/bulk_import.py:266 +#: dcim/forms/filtersets.py:382 msgid "Rack type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:373 netbox/dcim/forms/bulk_edit.py:721 -#: netbox/dcim/forms/bulk_edit.py:782 netbox/templates/dcim/device.html:104 -#: netbox/templates/dcim/module.html:77 netbox/templates/dcim/modulebay.html:70 -#: netbox/templates/dcim/rack.html:57 -#: netbox/templates/virtualization/virtualmachine.html:35 +#: dcim/forms/bulk_edit.py:384 dcim/forms/bulk_edit.py:765 +#: dcim/forms/bulk_edit.py:826 templates/dcim/device.html:104 +#: templates/dcim/module.html:77 templates/dcim/modulebay.html:70 +#: templates/dcim/rack.html:57 templates/virtualization/virtualmachine.html:35 msgid "Serial Number" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:376 netbox/dcim/forms/filtersets.py:388 -#: netbox/dcim/forms/filtersets.py:814 netbox/dcim/forms/filtersets.py:1016 -#: netbox/dcim/forms/filtersets.py:1610 +#: dcim/forms/bulk_edit.py:387 dcim/forms/filtersets.py:389 +#: dcim/forms/filtersets.py:833 dcim/forms/filtersets.py:1035 +#: dcim/forms/filtersets.py:1634 msgid "Asset tag" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:420 netbox/dcim/forms/bulk_edit.py:533 -#: netbox/dcim/forms/bulk_edit.py:577 netbox/dcim/forms/bulk_edit.py:714 -#: netbox/dcim/forms/bulk_import.py:291 netbox/dcim/forms/bulk_import.py:434 -#: netbox/dcim/forms/bulk_import.py:607 netbox/dcim/forms/filtersets.py:281 -#: netbox/dcim/forms/filtersets.py:512 netbox/dcim/forms/filtersets.py:670 -#: netbox/dcim/forms/filtersets.py:805 netbox/templates/dcim/device.html:98 -#: netbox/templates/dcim/devicetype.html:65 -#: netbox/templates/dcim/moduletype.html:43 netbox/templates/dcim/rack.html:65 -#: netbox/templates/dcim/racktype.html:28 +#: dcim/forms/bulk_edit.py:436 dcim/forms/bulk_edit.py:547 +#: dcim/forms/bulk_edit.py:615 dcim/forms/bulk_edit.py:758 +#: dcim/forms/bulk_import.py:295 dcim/forms/bulk_import.py:453 +#: dcim/forms/bulk_import.py:638 dcim/forms/filtersets.py:282 +#: dcim/forms/filtersets.py:513 dcim/forms/filtersets.py:684 +#: dcim/forms/filtersets.py:824 templates/dcim/device.html:98 +#: templates/dcim/devicetype.html:65 templates/dcim/moduletype.html:47 +#: templates/dcim/rack.html:65 templates/dcim/racktype.html:28 msgid "Airflow" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:449 netbox/dcim/forms/bulk_edit.py:928 -#: netbox/dcim/forms/bulk_import.py:346 netbox/dcim/forms/bulk_import.py:349 -#: netbox/dcim/forms/bulk_import.py:580 netbox/dcim/forms/bulk_import.py:1495 -#: netbox/dcim/forms/bulk_import.py:1499 netbox/dcim/forms/filtersets.py:105 -#: netbox/dcim/forms/filtersets.py:325 netbox/dcim/forms/filtersets.py:406 -#: netbox/dcim/forms/filtersets.py:420 netbox/dcim/forms/filtersets.py:458 -#: netbox/dcim/forms/filtersets.py:773 netbox/dcim/forms/filtersets.py:986 -#: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 -#: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 -#: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 -#: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 -#: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 -#: netbox/templates/dcim/inc/cable_termination.html:16 -#: netbox/templates/dcim/powerfeed.html:28 netbox/templates/dcim/rack.html:13 -#: netbox/templates/dcim/rack/base.html:4 -#: netbox/templates/dcim/rackreservation.html:19 -#: netbox/templates/dcim/rackreservation.html:36 -#: netbox/virtualization/forms/model_forms.py:113 +#: dcim/forms/bulk_edit.py:465 dcim/forms/bulk_edit.py:972 +#: dcim/forms/bulk_import.py:350 dcim/forms/bulk_import.py:353 +#: dcim/forms/bulk_import.py:611 dcim/forms/bulk_import.py:1526 +#: dcim/forms/bulk_import.py:1530 dcim/forms/filtersets.py:106 +#: dcim/forms/filtersets.py:326 dcim/forms/filtersets.py:407 +#: dcim/forms/filtersets.py:421 dcim/forms/filtersets.py:459 +#: dcim/forms/filtersets.py:792 dcim/forms/filtersets.py:1005 +#: dcim/forms/filtersets.py:1103 dcim/forms/filtersets.py:1235 +#: dcim/forms/model_forms.py:278 dcim/forms/model_forms.py:322 +#: dcim/forms/model_forms.py:583 dcim/forms/model_forms.py:861 +#: dcim/forms/object_create.py:404 dcim/tables/devices.py:171 +#: dcim/tables/power.py:70 dcim/tables/racks.py:225 +#: ipam/forms/filtersets.py:467 templates/dcim/device.html:30 +#: templates/dcim/inc/cable_termination.html:16 +#: templates/dcim/powerfeed.html:28 templates/dcim/rack.html:13 +#: templates/dcim/rack/base.html:4 templates/dcim/rackreservation.html:19 +#: templates/dcim/rackreservation.html:36 +#: virtualization/forms/model_forms.py:113 msgid "Rack" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:453 netbox/dcim/forms/bulk_edit.py:747 -#: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 -#: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 -#: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 -#: netbox/templates/dcim/device_edit.html:22 +#: dcim/forms/bulk_edit.py:468 dcim/forms/bulk_edit.py:791 +#: dcim/forms/filtersets.py:327 dcim/forms/filtersets.py:400 +#: dcim/forms/filtersets.py:483 dcim/forms/filtersets.py:618 +#: dcim/forms/filtersets.py:741 dcim/forms/filtersets.py:963 +#: dcim/forms/model_forms.py:446 dcim/forms/model_forms.py:775 +#: dcim/forms/model_forms.py:1757 templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:509 netbox/dcim/forms/bulk_import.py:401 -#: netbox/dcim/forms/filtersets.py:500 netbox/dcim/forms/model_forms.py:362 +#: dcim/forms/bulk_edit.py:523 dcim/forms/bulk_import.py:405 +#: dcim/forms/filtersets.py:501 dcim/forms/model_forms.py:370 msgid "Default platform" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:514 netbox/dcim/forms/bulk_edit.py:573 -#: netbox/dcim/forms/filtersets.py:503 netbox/dcim/forms/filtersets.py:623 +#: dcim/forms/bulk_edit.py:528 dcim/forms/bulk_edit.py:611 +#: dcim/forms/filtersets.py:504 dcim/forms/filtersets.py:637 msgid "Part number" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:518 +#: dcim/forms/bulk_edit.py:532 msgid "U height" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:530 netbox/dcim/tables/devicetypes.py:107 +#: dcim/forms/bulk_edit.py:544 dcim/tables/devicetypes.py:107 msgid "Exclude from utilization" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 -#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 -#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 -#: netbox/dcim/forms/model_forms.py:1085 netbox/dcim/forms/object_create.py:123 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 -#: netbox/templates/dcim/devicebay.html:52 netbox/templates/dcim/module.html:61 +#: dcim/forms/bulk_edit.py:573 dcim/forms/model_forms.py:385 +#: dcim/forms/model_forms.py:1014 dcim/forms/model_forms.py:1056 +#: dcim/forms/model_forms.py:1083 dcim/forms/model_forms.py:1111 +#: dcim/forms/model_forms.py:1142 dcim/forms/model_forms.py:1161 +#: dcim/forms/model_forms.py:1179 dcim/forms/object_create.py:123 +#: dcim/tables/devicetypes.py:82 templates/dcim/device.html:88 +#: templates/dcim/devicebay.html:52 templates/dcim/module.html:61 msgid "Device Type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 -#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 -#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 -#: netbox/dcim/forms/model_forms.py:1086 netbox/dcim/forms/object_create.py:124 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 netbox/templates/dcim/modulebay.html:66 -#: netbox/templates/dcim/moduletype.html:24 +#: dcim/forms/bulk_edit.py:582 dcim/forms/model_forms.py:412 +#: templates/dcim/moduletypeprofile.html:32 +msgid "Schema" +msgstr "" + +#: dcim/forms/bulk_edit.py:594 dcim/forms/bulk_edit.py:601 +#: dcim/forms/bulk_import.py:442 dcim/forms/filtersets.py:629 +#: dcim/forms/model_forms.py:419 dcim/forms/model_forms.py:432 +#: dcim/tables/modules.py:45 templates/account/base.html:7 +#: templates/dcim/moduletype.html:27 templates/inc/user_menu.html:40 +#: vpn/forms/bulk_edit.py:255 vpn/forms/filtersets.py:194 +#: vpn/forms/model_forms.py:382 +msgid "Profile" +msgstr "" + +#: dcim/forms/bulk_edit.py:639 dcim/forms/model_forms.py:445 +#: dcim/forms/model_forms.py:1015 dcim/forms/model_forms.py:1057 +#: dcim/forms/model_forms.py:1084 dcim/forms/model_forms.py:1112 +#: dcim/forms/model_forms.py:1143 dcim/forms/model_forms.py:1162 +#: dcim/forms/model_forms.py:1180 dcim/forms/object_create.py:124 +#: dcim/tables/modules.py:54 dcim/tables/modules.py:100 +#: templates/dcim/module.html:92 templates/dcim/modulebay.html:66 +#: templates/dcim/moduletype.html:24 msgid "Module Type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:605 netbox/dcim/forms/model_forms.py:380 -#: netbox/dcim/forms/model_forms.py:411 -#: netbox/templates/dcim/devicetype.html:11 +#: dcim/forms/bulk_edit.py:643 dcim/forms/model_forms.py:388 +#: templates/dcim/devicetype.html:11 msgid "Chassis" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:619 netbox/dcim/models/devices.py:483 -#: netbox/dcim/tables/devices.py:78 +#: dcim/forms/bulk_edit.py:662 dcim/models/devices.py:386 +#: dcim/tables/devices.py:78 msgid "VM role" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:622 netbox/dcim/forms/bulk_edit.py:646 -#: netbox/dcim/forms/bulk_edit.py:729 netbox/dcim/forms/bulk_import.py:461 -#: netbox/dcim/forms/bulk_import.py:465 netbox/dcim/forms/bulk_import.py:484 -#: netbox/dcim/forms/bulk_import.py:488 netbox/dcim/forms/bulk_import.py:613 -#: netbox/dcim/forms/bulk_import.py:617 netbox/dcim/forms/filtersets.py:690 -#: netbox/dcim/forms/filtersets.py:706 netbox/dcim/forms/filtersets.py:824 -#: netbox/dcim/forms/model_forms.py:424 netbox/dcim/forms/model_forms.py:451 -#: netbox/dcim/forms/model_forms.py:566 -#: netbox/virtualization/forms/bulk_import.py:138 -#: netbox/virtualization/forms/bulk_import.py:139 -#: netbox/virtualization/forms/filtersets.py:194 -#: netbox/virtualization/forms/model_forms.py:222 +#: dcim/forms/bulk_edit.py:665 dcim/forms/bulk_edit.py:690 +#: dcim/forms/bulk_edit.py:773 dcim/forms/bulk_import.py:490 +#: dcim/forms/bulk_import.py:494 dcim/forms/bulk_import.py:515 +#: dcim/forms/bulk_import.py:519 dcim/forms/bulk_import.py:644 +#: dcim/forms/bulk_import.py:648 dcim/forms/filtersets.py:704 +#: dcim/forms/filtersets.py:725 dcim/forms/filtersets.py:843 +#: dcim/forms/model_forms.py:511 dcim/forms/model_forms.py:545 +#: dcim/forms/model_forms.py:660 virtualization/forms/bulk_import.py:138 +#: virtualization/forms/bulk_import.py:139 +#: virtualization/forms/filtersets.py:194 +#: virtualization/forms/model_forms.py:222 msgid "Config template" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:670 netbox/dcim/forms/bulk_edit.py:1079 -#: netbox/dcim/forms/bulk_import.py:519 netbox/dcim/forms/filtersets.py:115 -#: netbox/dcim/forms/model_forms.py:511 netbox/dcim/forms/model_forms.py:884 -#: netbox/dcim/forms/model_forms.py:901 netbox/extras/filtersets.py:547 +#: dcim/forms/bulk_edit.py:714 dcim/forms/bulk_edit.py:1123 +#: dcim/forms/bulk_import.py:550 dcim/forms/filtersets.py:116 +#: dcim/forms/model_forms.py:605 dcim/forms/model_forms.py:978 +#: dcim/forms/model_forms.py:995 extras/filtersets.py:640 msgid "Device type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:681 netbox/dcim/forms/bulk_import.py:500 -#: netbox/dcim/forms/filtersets.py:120 netbox/dcim/forms/model_forms.py:519 +#: dcim/forms/bulk_edit.py:725 dcim/forms/bulk_import.py:531 +#: dcim/forms/filtersets.py:121 dcim/forms/model_forms.py:613 msgid "Device role" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:704 netbox/dcim/forms/bulk_import.py:525 -#: netbox/dcim/forms/filtersets.py:797 netbox/dcim/forms/model_forms.py:461 -#: netbox/dcim/forms/model_forms.py:524 netbox/dcim/tables/devices.py:192 -#: netbox/extras/filtersets.py:563 netbox/templates/dcim/device.html:186 -#: netbox/templates/dcim/platform.html:26 -#: netbox/templates/virtualization/virtualmachine.html:27 -#: netbox/virtualization/forms/bulk_edit.py:142 -#: netbox/virtualization/forms/bulk_import.py:128 -#: netbox/virtualization/forms/filtersets.py:174 -#: netbox/virtualization/forms/model_forms.py:210 -#: netbox/virtualization/tables/virtualmachines.py:49 +#: dcim/forms/bulk_edit.py:748 dcim/forms/bulk_import.py:556 +#: dcim/forms/filtersets.py:816 dcim/forms/model_forms.py:555 +#: dcim/forms/model_forms.py:618 dcim/tables/devices.py:192 +#: extras/filtersets.py:656 templates/dcim/device.html:186 +#: templates/dcim/platform.html:26 +#: templates/virtualization/virtualmachine.html:27 +#: virtualization/forms/bulk_edit.py:142 +#: virtualization/forms/bulk_import.py:128 +#: virtualization/forms/filtersets.py:174 +#: virtualization/forms/model_forms.py:210 +#: virtualization/tables/virtualmachines.py:49 msgid "Platform" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:734 netbox/dcim/forms/bulk_import.py:544 -#: netbox/dcim/forms/filtersets.py:729 netbox/dcim/forms/filtersets.py:899 -#: netbox/dcim/forms/model_forms.py:533 netbox/dcim/tables/devices.py:212 -#: netbox/extras/filtersets.py:596 netbox/extras/forms/filtersets.py:329 -#: netbox/ipam/forms/filtersets.py:432 netbox/ipam/forms/filtersets.py:464 -#: netbox/templates/dcim/device.html:239 -#: netbox/templates/virtualization/cluster.html:10 -#: netbox/templates/virtualization/virtualmachine.html:92 -#: netbox/templates/virtualization/virtualmachine.html:101 -#: netbox/virtualization/filtersets.py:123 -#: netbox/virtualization/filtersets.py:243 -#: netbox/virtualization/forms/bulk_edit.py:111 -#: netbox/virtualization/forms/bulk_import.py:98 -#: netbox/virtualization/forms/filtersets.py:105 -#: netbox/virtualization/forms/filtersets.py:129 -#: netbox/virtualization/forms/filtersets.py:212 -#: netbox/virtualization/forms/model_forms.py:78 -#: netbox/virtualization/forms/model_forms.py:183 -#: netbox/virtualization/tables/virtualmachines.py:37 +#: dcim/forms/bulk_edit.py:778 dcim/forms/bulk_import.py:575 +#: dcim/forms/filtersets.py:748 dcim/forms/filtersets.py:918 +#: dcim/forms/model_forms.py:627 dcim/tables/devices.py:212 +#: extras/filtersets.py:689 extras/forms/filtersets.py:364 +#: ipam/forms/filtersets.py:439 ipam/forms/filtersets.py:472 +#: templates/dcim/device.html:239 templates/virtualization/cluster.html:10 +#: templates/virtualization/virtualmachine.html:92 +#: templates/virtualization/virtualmachine.html:101 +#: virtualization/filtersets.py:123 virtualization/filtersets.py:245 +#: virtualization/forms/bulk_edit.py:111 virtualization/forms/bulk_import.py:98 +#: virtualization/forms/filtersets.py:105 +#: virtualization/forms/filtersets.py:129 +#: virtualization/forms/filtersets.py:212 +#: virtualization/forms/model_forms.py:78 +#: virtualization/forms/model_forms.py:183 +#: virtualization/tables/virtualmachines.py:37 msgid "Cluster" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:748 -#: netbox/templates/extras/dashboard/widget_config.html:7 -#: netbox/virtualization/forms/bulk_edit.py:173 +#: dcim/forms/bulk_edit.py:792 templates/extras/dashboard/widget_config.html:7 +#: virtualization/forms/bulk_edit.py:173 msgid "Configuration" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:749 netbox/netbox/navigation/menu.py:251 -#: netbox/templates/dcim/device_edit.html:80 +#: dcim/forms/bulk_edit.py:793 netbox/navigation/menu.py:252 +#: templates/dcim/device_edit.html:80 msgid "Virtualization" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:763 netbox/dcim/forms/bulk_import.py:680 -#: netbox/dcim/forms/model_forms.py:658 netbox/dcim/forms/model_forms.py:909 +#: dcim/forms/bulk_edit.py:807 dcim/forms/bulk_import.py:711 +#: dcim/forms/model_forms.py:752 dcim/forms/model_forms.py:1003 msgid "Module type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:817 netbox/dcim/forms/bulk_edit.py:1002 -#: netbox/dcim/forms/bulk_edit.py:1021 netbox/dcim/forms/bulk_edit.py:1044 -#: netbox/dcim/forms/bulk_edit.py:1086 netbox/dcim/forms/bulk_edit.py:1130 -#: netbox/dcim/forms/bulk_edit.py:1181 netbox/dcim/forms/bulk_edit.py:1208 -#: netbox/dcim/forms/bulk_edit.py:1235 netbox/dcim/forms/bulk_edit.py:1253 -#: netbox/dcim/forms/bulk_edit.py:1271 netbox/dcim/forms/filtersets.py:68 -#: netbox/dcim/forms/object_create.py:46 netbox/templates/dcim/cable.html:32 -#: netbox/templates/dcim/consoleport.html:32 -#: netbox/templates/dcim/consoleserverport.html:32 -#: netbox/templates/dcim/devicebay.html:28 -#: netbox/templates/dcim/frontport.html:32 -#: netbox/templates/dcim/inc/panels/inventory_items.html:19 -#: netbox/templates/dcim/interface.html:42 -#: netbox/templates/dcim/inventoryitem.html:32 -#: netbox/templates/dcim/modulebay.html:34 -#: netbox/templates/dcim/poweroutlet.html:32 -#: netbox/templates/dcim/powerport.html:32 -#: netbox/templates/dcim/rearport.html:32 -#: netbox/templates/extras/customfield.html:26 -#: netbox/templates/generic/bulk_import.html:162 +#: dcim/forms/bulk_edit.py:861 dcim/forms/bulk_edit.py:1046 +#: dcim/forms/bulk_edit.py:1065 dcim/forms/bulk_edit.py:1088 +#: dcim/forms/bulk_edit.py:1130 dcim/forms/bulk_edit.py:1174 +#: dcim/forms/bulk_edit.py:1225 dcim/forms/bulk_edit.py:1252 +#: dcim/forms/bulk_edit.py:1279 dcim/forms/bulk_edit.py:1297 +#: dcim/forms/bulk_edit.py:1315 dcim/forms/filtersets.py:69 +#: dcim/forms/object_create.py:46 templates/dcim/cable.html:32 +#: templates/dcim/consoleport.html:32 templates/dcim/consoleserverport.html:32 +#: templates/dcim/devicebay.html:28 templates/dcim/frontport.html:32 +#: templates/dcim/inc/panels/inventory_items.html:19 +#: templates/dcim/interface.html:42 templates/dcim/inventoryitem.html:32 +#: templates/dcim/modulebay.html:34 templates/dcim/poweroutlet.html:32 +#: templates/dcim/powerport.html:32 templates/dcim/rearport.html:32 +#: templates/extras/customfield.html:26 templates/generic/bulk_import.html:162 msgid "Label" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:826 netbox/dcim/forms/filtersets.py:1117 -#: netbox/templates/dcim/cable.html:50 +#: dcim/forms/bulk_edit.py:870 dcim/forms/filtersets.py:1136 +#: templates/dcim/cable.html:50 msgid "Length" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:831 netbox/dcim/forms/bulk_import.py:1363 -#: netbox/dcim/forms/bulk_import.py:1366 netbox/dcim/forms/filtersets.py:1121 +#: dcim/forms/bulk_edit.py:875 dcim/forms/bulk_import.py:1394 +#: dcim/forms/bulk_import.py:1397 dcim/forms/filtersets.py:1140 msgid "Length unit" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:855 -#: netbox/templates/dcim/virtualchassis.html:23 +#: dcim/forms/bulk_edit.py:899 templates/dcim/virtualchassis.html:23 msgid "Domain" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:923 netbox/dcim/forms/bulk_import.py:1482 -#: netbox/dcim/forms/filtersets.py:1207 netbox/dcim/forms/model_forms.py:761 +#: dcim/forms/bulk_edit.py:967 dcim/forms/bulk_import.py:1513 +#: dcim/forms/filtersets.py:1226 dcim/forms/model_forms.py:855 msgid "Power panel" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:945 netbox/dcim/forms/bulk_import.py:1518 -#: netbox/dcim/forms/filtersets.py:1229 netbox/templates/dcim/powerfeed.html:83 +#: dcim/forms/bulk_edit.py:989 dcim/forms/bulk_import.py:1549 +#: dcim/forms/filtersets.py:1248 templates/dcim/powerfeed.html:83 msgid "Supply" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:951 netbox/dcim/forms/bulk_import.py:1523 -#: netbox/dcim/forms/filtersets.py:1234 netbox/templates/dcim/powerfeed.html:95 +#: dcim/forms/bulk_edit.py:995 dcim/forms/bulk_import.py:1554 +#: dcim/forms/filtersets.py:1253 templates/dcim/powerfeed.html:95 msgid "Phase" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:957 netbox/dcim/forms/filtersets.py:1239 -#: netbox/templates/dcim/powerfeed.html:87 +#: dcim/forms/bulk_edit.py:1001 dcim/forms/filtersets.py:1258 +#: templates/dcim/powerfeed.html:87 msgid "Voltage" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:961 netbox/dcim/forms/filtersets.py:1243 -#: netbox/templates/dcim/powerfeed.html:91 +#: dcim/forms/bulk_edit.py:1005 dcim/forms/filtersets.py:1262 +#: templates/dcim/powerfeed.html:91 msgid "Amperage" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:965 netbox/dcim/forms/filtersets.py:1247 +#: dcim/forms/bulk_edit.py:1009 dcim/forms/filtersets.py:1266 msgid "Max utilization" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1054 +#: dcim/forms/bulk_edit.py:1098 msgid "Maximum draw" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1057 -#: netbox/dcim/models/device_component_templates.py:281 -#: netbox/dcim/models/device_components.py:352 +#: dcim/forms/bulk_edit.py:1101 dcim/models/device_component_templates.py:281 +#: dcim/models/device_components.py:352 msgid "Maximum power draw (watts)" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1060 +#: dcim/forms/bulk_edit.py:1104 msgid "Allocated draw" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1063 -#: netbox/dcim/models/device_component_templates.py:288 -#: netbox/dcim/models/device_components.py:359 +#: dcim/forms/bulk_edit.py:1107 dcim/models/device_component_templates.py:288 +#: dcim/models/device_components.py:359 msgid "Allocated power draw (watts)" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 -#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 +#: dcim/forms/bulk_edit.py:1140 dcim/forms/bulk_import.py:844 +#: dcim/forms/model_forms.py:1072 dcim/forms/model_forms.py:1426 +#: dcim/forms/model_forms.py:1741 dcim/forms/object_import.py:55 msgid "Power port" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1101 netbox/dcim/forms/bulk_import.py:820 +#: dcim/forms/bulk_edit.py:1145 dcim/forms/bulk_import.py:851 msgid "Feed leg" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1147 netbox/dcim/forms/bulk_edit.py:1465 +#: dcim/forms/bulk_edit.py:1191 dcim/forms/bulk_edit.py:1512 msgid "Management only" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1157 netbox/dcim/forms/bulk_edit.py:1471 -#: netbox/dcim/forms/bulk_import.py:906 netbox/dcim/forms/filtersets.py:1448 -#: netbox/dcim/forms/object_import.py:90 -#: netbox/dcim/models/device_component_templates.py:445 -#: netbox/dcim/models/device_components.py:724 +#: dcim/forms/bulk_edit.py:1201 dcim/forms/bulk_edit.py:1518 +#: dcim/forms/bulk_import.py:937 dcim/forms/filtersets.py:1472 +#: dcim/forms/object_import.py:90 dcim/models/device_component_templates.py:445 +#: dcim/models/device_components.py:733 msgid "PoE mode" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1163 netbox/dcim/forms/bulk_edit.py:1477 -#: netbox/dcim/forms/bulk_import.py:912 netbox/dcim/forms/filtersets.py:1453 -#: netbox/dcim/forms/object_import.py:95 -#: netbox/dcim/models/device_component_templates.py:452 -#: netbox/dcim/models/device_components.py:731 +#: dcim/forms/bulk_edit.py:1207 dcim/forms/bulk_edit.py:1524 +#: dcim/forms/bulk_import.py:943 dcim/forms/filtersets.py:1477 +#: dcim/forms/object_import.py:95 dcim/models/device_component_templates.py:452 +#: dcim/models/device_components.py:740 msgid "PoE type" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1169 netbox/dcim/forms/filtersets.py:1468 -#: netbox/dcim/forms/object_import.py:100 +#: dcim/forms/bulk_edit.py:1213 dcim/forms/filtersets.py:1492 +#: dcim/forms/object_import.py:100 msgid "Wireless role" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 -#: netbox/templates/dcim/consoleport.html:24 -#: netbox/templates/dcim/consoleserverport.html:24 -#: netbox/templates/dcim/frontport.html:24 -#: netbox/templates/dcim/interface.html:34 netbox/templates/dcim/module.html:54 -#: netbox/templates/dcim/modulebay.html:26 -#: netbox/templates/dcim/modulebay.html:58 -#: netbox/templates/dcim/poweroutlet.html:24 -#: netbox/templates/dcim/powerport.html:24 -#: netbox/templates/dcim/rearport.html:24 +#: dcim/forms/bulk_edit.py:1350 dcim/forms/model_forms.py:774 +#: dcim/forms/model_forms.py:1371 dcim/tables/devices.py:322 +#: templates/dcim/consoleport.html:24 templates/dcim/consoleserverport.html:24 +#: templates/dcim/frontport.html:24 templates/dcim/interface.html:34 +#: templates/dcim/module.html:54 templates/dcim/modulebay.html:26 +#: templates/dcim/modulebay.html:58 templates/dcim/poweroutlet.html:24 +#: templates/dcim/powerport.html:24 templates/dcim/rearport.html:24 msgid "Module" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1445 netbox/dcim/tables/devices.py:698 -#: netbox/templates/dcim/interface.html:116 +#: dcim/forms/bulk_edit.py:1492 dcim/tables/devices.py:705 +#: templates/dcim/interface.html:116 msgid "LAG" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 +#: dcim/forms/bulk_edit.py:1497 dcim/forms/model_forms.py:1453 msgid "Virtual device contexts" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1456 netbox/dcim/forms/bulk_import.py:741 -#: netbox/dcim/forms/bulk_import.py:767 netbox/dcim/forms/filtersets.py:1301 -#: netbox/dcim/forms/filtersets.py:1326 netbox/dcim/forms/filtersets.py:1412 -#: netbox/dcim/tables/devices.py:631 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:62 -#: netbox/templates/dcim/consoleport.html:40 -#: netbox/templates/dcim/consoleserverport.html:40 +#: dcim/forms/bulk_edit.py:1503 dcim/forms/bulk_import.py:772 +#: dcim/forms/bulk_import.py:798 dcim/forms/filtersets.py:1320 +#: dcim/forms/filtersets.py:1345 dcim/forms/filtersets.py:1436 +#: dcim/tables/devices.py:638 +#: templates/circuits/inc/circuit_termination_fields.html:62 +#: templates/dcim/consoleport.html:40 templates/dcim/consoleserverport.html:40 msgid "Speed" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1485 netbox/dcim/forms/bulk_import.py:915 -#: netbox/templates/vpn/ikepolicy.html:25 -#: netbox/templates/vpn/ipsecprofile.html:21 -#: netbox/templates/vpn/ipsecprofile.html:48 -#: netbox/virtualization/forms/bulk_edit.py:215 -#: netbox/virtualization/forms/bulk_import.py:171 -#: netbox/vpn/forms/bulk_edit.py:146 netbox/vpn/forms/bulk_edit.py:232 -#: netbox/vpn/forms/bulk_import.py:176 netbox/vpn/forms/bulk_import.py:234 -#: netbox/vpn/forms/filtersets.py:140 netbox/vpn/forms/filtersets.py:183 -#: netbox/vpn/forms/filtersets.py:197 netbox/vpn/tables/crypto.py:64 -#: netbox/vpn/tables/crypto.py:162 +#: dcim/forms/bulk_edit.py:1532 dcim/forms/bulk_import.py:946 +#: templates/vpn/ikepolicy.html:25 templates/vpn/ipsecprofile.html:21 +#: templates/vpn/ipsecprofile.html:48 virtualization/forms/bulk_edit.py:215 +#: virtualization/forms/bulk_import.py:171 vpn/forms/bulk_edit.py:146 +#: vpn/forms/bulk_edit.py:232 vpn/forms/bulk_import.py:176 +#: vpn/forms/bulk_import.py:234 vpn/forms/filtersets.py:140 +#: vpn/forms/filtersets.py:183 vpn/forms/filtersets.py:197 +#: vpn/tables/crypto.py:64 vpn/tables/crypto.py:162 msgid "Mode" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 -#: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 -#: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 -#: netbox/virtualization/forms/model_forms.py:335 +#: dcim/forms/bulk_edit.py:1540 dcim/forms/model_forms.py:1502 +#: ipam/forms/bulk_import.py:174 ipam/forms/filtersets.py:561 +#: ipam/models/vlans.py:93 virtualization/forms/bulk_edit.py:222 +#: virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 -#: netbox/dcim/tables/devices.py:592 -#: netbox/virtualization/forms/bulk_edit.py:230 -#: netbox/virtualization/forms/model_forms.py:340 +#: dcim/forms/bulk_edit.py:1549 dcim/forms/model_forms.py:1508 +#: dcim/tables/devices.py:599 virtualization/forms/bulk_edit.py:230 +#: virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 -#: netbox/dcim/tables/devices.py:598 -#: netbox/virtualization/forms/bulk_edit.py:238 -#: netbox/virtualization/forms/model_forms.py:349 +#: dcim/forms/bulk_edit.py:1558 dcim/forms/model_forms.py:1517 +#: dcim/tables/devices.py:605 virtualization/forms/bulk_edit.py:238 +#: virtualization/forms/model_forms.py:349 msgid "Tagged VLANs" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1514 +#: dcim/forms/bulk_edit.py:1561 msgid "Add tagged VLANs" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1523 +#: dcim/forms/bulk_edit.py:1570 msgid "Remove tagged VLANs" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 -#: netbox/virtualization/forms/model_forms.py:358 +#: dcim/forms/bulk_edit.py:1581 dcim/forms/model_forms.py:1526 +#: virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 +#: dcim/forms/bulk_edit.py:1596 dcim/forms/model_forms.py:1489 msgid "Wireless LAN group" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 -#: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 -#: netbox/templates/dcim/interface.html:337 -#: netbox/wireless/tables/wirelesslan.py:24 +#: dcim/forms/bulk_edit.py:1601 dcim/forms/model_forms.py:1494 +#: dcim/tables/devices.py:647 netbox/navigation/menu.py:153 +#: templates/dcim/interface.html:337 wireless/tables/wirelesslan.py:24 msgid "Wireless LANs" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 -#: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 -#: netbox/netbox/navigation/menu.py:108 -#: netbox/templates/dcim/interface.html:128 -#: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:76 -#: netbox/virtualization/forms/filtersets.py:205 -#: netbox/virtualization/forms/model_forms.py:378 +#: dcim/forms/bulk_edit.py:1610 dcim/forms/filtersets.py:1405 +#: dcim/forms/model_forms.py:1560 ipam/forms/bulk_edit.py:269 +#: ipam/forms/bulk_edit.py:367 ipam/forms/filtersets.py:177 +#: netbox/navigation/menu.py:109 templates/dcim/interface.html:128 +#: templates/ipam/prefix.html:91 templates/virtualization/vminterface.html:76 +#: virtualization/forms/filtersets.py:205 +#: virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1467 -#: netbox/virtualization/forms/model_forms.py:379 +#: dcim/forms/bulk_edit.py:1611 dcim/forms/filtersets.py:740 +#: dcim/forms/model_forms.py:1561 virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 +#: dcim/forms/bulk_edit.py:1612 dcim/forms/filtersets.py:1406 +#: dcim/forms/model_forms.py:1116 dcim/forms/model_forms.py:1563 msgid "PoE" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 -#: netbox/templates/dcim/interface.html:105 -#: netbox/virtualization/forms/bulk_edit.py:254 -#: netbox/virtualization/forms/model_forms.py:380 +#: dcim/forms/bulk_edit.py:1613 dcim/forms/model_forms.py:1562 +#: templates/dcim/interface.html:105 virtualization/forms/bulk_edit.py:254 +#: virtualization/forms/model_forms.py:380 msgid "Related Interfaces" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1472 -#: netbox/virtualization/forms/bulk_edit.py:257 -#: netbox/virtualization/forms/filtersets.py:206 -#: netbox/virtualization/forms/model_forms.py:383 +#: dcim/forms/bulk_edit.py:1615 dcim/forms/filtersets.py:1407 +#: dcim/forms/model_forms.py:1566 virtualization/forms/bulk_edit.py:257 +#: virtualization/forms/filtersets.py:206 +#: virtualization/forms/model_forms.py:383 msgid "802.1Q Switching" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1573 +#: dcim/forms/bulk_edit.py:1620 msgid "Add/Remove" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1632 netbox/dcim/forms/bulk_edit.py:1634 +#: dcim/forms/bulk_edit.py:1679 dcim/forms/bulk_edit.py:1681 msgid "Interface mode must be specified to assign VLANs" msgstr "" -#: netbox/dcim/forms/bulk_edit.py:1639 +#: dcim/forms/bulk_edit.py:1686 msgid "An access interface cannot have tagged VLANs assigned." msgstr "" -#: netbox/dcim/forms/bulk_import.py:66 +#: dcim/forms/bulk_import.py:67 msgid "Name of parent region" msgstr "" -#: netbox/dcim/forms/bulk_import.py:80 +#: dcim/forms/bulk_import.py:81 msgid "Name of parent site group" msgstr "" -#: netbox/dcim/forms/bulk_import.py:99 +#: dcim/forms/bulk_import.py:100 msgid "Assigned region" msgstr "" -#: netbox/dcim/forms/bulk_import.py:106 netbox/tenancy/forms/bulk_import.py:44 -#: netbox/tenancy/forms/bulk_import.py:85 -#: netbox/wireless/forms/bulk_import.py:42 +#: dcim/forms/bulk_import.py:107 tenancy/forms/bulk_import.py:44 +#: wireless/forms/bulk_import.py:42 msgid "Assigned group" msgstr "" -#: netbox/dcim/forms/bulk_import.py:125 +#: dcim/forms/bulk_import.py:126 msgid "available options" msgstr "" -#: netbox/dcim/forms/bulk_import.py:136 netbox/dcim/forms/bulk_import.py:570 -#: netbox/dcim/forms/bulk_import.py:1479 netbox/ipam/forms/bulk_import.py:472 -#: netbox/virtualization/forms/bulk_import.py:64 -#: netbox/virtualization/forms/bulk_import.py:95 +#: dcim/forms/bulk_import.py:137 dcim/forms/bulk_import.py:601 +#: dcim/forms/bulk_import.py:1510 ipam/forms/bulk_import.py:479 +#: virtualization/forms/bulk_import.py:64 +#: virtualization/forms/bulk_import.py:95 msgid "Assigned site" msgstr "" -#: netbox/dcim/forms/bulk_import.py:143 +#: dcim/forms/bulk_import.py:144 msgid "Parent location" msgstr "" -#: netbox/dcim/forms/bulk_import.py:145 +#: dcim/forms/bulk_import.py:146 msgid "Location not found." msgstr "" -#: netbox/dcim/forms/bulk_import.py:187 +#: dcim/forms/bulk_import.py:191 msgid "The manufacturer of this rack type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:198 +#: dcim/forms/bulk_import.py:202 msgid "The lowest-numbered position in the rack" msgstr "" -#: netbox/dcim/forms/bulk_import.py:203 netbox/dcim/forms/bulk_import.py:278 +#: dcim/forms/bulk_import.py:207 dcim/forms/bulk_import.py:282 msgid "Rail-to-rail width (in inches)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:209 netbox/dcim/forms/bulk_import.py:288 +#: dcim/forms/bulk_import.py:213 dcim/forms/bulk_import.py:292 msgid "Unit for outer dimensions" msgstr "" -#: netbox/dcim/forms/bulk_import.py:215 netbox/dcim/forms/bulk_import.py:300 +#: dcim/forms/bulk_import.py:219 dcim/forms/bulk_import.py:304 msgid "Unit for rack weights" msgstr "" -#: netbox/dcim/forms/bulk_import.py:247 +#: dcim/forms/bulk_import.py:251 msgid "Name of assigned tenant" msgstr "" -#: netbox/dcim/forms/bulk_import.py:259 +#: dcim/forms/bulk_import.py:263 msgid "Name of assigned role" msgstr "" -#: netbox/dcim/forms/bulk_import.py:266 +#: dcim/forms/bulk_import.py:270 msgid "Rack type model" msgstr "" -#: netbox/dcim/forms/bulk_import.py:294 netbox/dcim/forms/bulk_import.py:437 -#: netbox/dcim/forms/bulk_import.py:610 +#: dcim/forms/bulk_import.py:298 dcim/forms/bulk_import.py:456 +#: dcim/forms/bulk_import.py:641 msgid "Airflow direction" msgstr "" -#: netbox/dcim/forms/bulk_import.py:326 +#: dcim/forms/bulk_import.py:330 msgid "Width must be set if not specifying a rack type." msgstr "" -#: netbox/dcim/forms/bulk_import.py:328 +#: dcim/forms/bulk_import.py:332 msgid "U height must be set if not specifying a rack type." msgstr "" -#: netbox/dcim/forms/bulk_import.py:336 +#: dcim/forms/bulk_import.py:340 msgid "Parent site" msgstr "" -#: netbox/dcim/forms/bulk_import.py:343 netbox/dcim/forms/bulk_import.py:1492 +#: dcim/forms/bulk_import.py:347 dcim/forms/bulk_import.py:1523 msgid "Rack's location (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:352 netbox/dcim/forms/model_forms.py:319 -#: netbox/dcim/tables/racks.py:221 -#: netbox/templates/dcim/rackreservation.html:12 -#: netbox/templates/dcim/rackreservation.html:45 +#: dcim/forms/bulk_import.py:356 dcim/forms/model_forms.py:327 +#: dcim/tables/racks.py:230 templates/dcim/rackreservation.html:12 +#: templates/dcim/rackreservation.html:45 msgid "Units" msgstr "" -#: netbox/dcim/forms/bulk_import.py:355 +#: dcim/forms/bulk_import.py:359 msgid "Comma-separated list of individual unit numbers" msgstr "" -#: netbox/dcim/forms/bulk_import.py:398 +#: dcim/forms/bulk_import.py:402 msgid "The manufacturer which produces this device type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:405 +#: dcim/forms/bulk_import.py:409 msgid "The default platform for devices of this type (optional)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:410 +#: dcim/forms/bulk_import.py:414 msgid "Device weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:416 +#: dcim/forms/bulk_import.py:420 msgid "Unit for device weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:442 +#: dcim/forms/bulk_import.py:461 msgid "Module weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:448 +#: dcim/forms/bulk_import.py:467 msgid "Unit for module weight" msgstr "" -#: netbox/dcim/forms/bulk_import.py:481 +#: dcim/forms/bulk_import.py:484 +msgid "Parent Device Role" +msgstr "" + +#: dcim/forms/bulk_import.py:486 +msgid "Device role not found." +msgstr "" + +#: dcim/forms/bulk_import.py:512 msgid "Limit platform assignments to this manufacturer" msgstr "" -#: netbox/dcim/forms/bulk_import.py:503 netbox/dcim/forms/bulk_import.py:1562 -#: netbox/tenancy/forms/bulk_import.py:106 +#: dcim/forms/bulk_import.py:534 dcim/forms/bulk_import.py:1593 +#: tenancy/forms/bulk_import.py:105 msgid "Assigned role" msgstr "" -#: netbox/dcim/forms/bulk_import.py:516 +#: dcim/forms/bulk_import.py:547 msgid "Device type manufacturer" msgstr "" -#: netbox/dcim/forms/bulk_import.py:522 +#: dcim/forms/bulk_import.py:553 msgid "Device type model" msgstr "" -#: netbox/dcim/forms/bulk_import.py:529 -#: netbox/virtualization/forms/bulk_import.py:132 +#: dcim/forms/bulk_import.py:560 virtualization/forms/bulk_import.py:132 msgid "Assigned platform" msgstr "" -#: netbox/dcim/forms/bulk_import.py:537 netbox/dcim/forms/bulk_import.py:541 -#: netbox/dcim/forms/model_forms.py:547 +#: dcim/forms/bulk_import.py:568 dcim/forms/bulk_import.py:572 +#: dcim/forms/model_forms.py:641 msgid "Virtual chassis" msgstr "" -#: netbox/dcim/forms/bulk_import.py:548 +#: dcim/forms/bulk_import.py:579 msgid "Virtualization cluster" msgstr "" -#: netbox/dcim/forms/bulk_import.py:577 +#: dcim/forms/bulk_import.py:608 msgid "Assigned location (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:584 +#: dcim/forms/bulk_import.py:615 msgid "Assigned rack (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:587 +#: dcim/forms/bulk_import.py:618 msgid "Face" msgstr "" -#: netbox/dcim/forms/bulk_import.py:590 +#: dcim/forms/bulk_import.py:621 msgid "Mounted rack face" msgstr "" -#: netbox/dcim/forms/bulk_import.py:597 +#: dcim/forms/bulk_import.py:628 msgid "Parent device (for child devices)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:600 +#: dcim/forms/bulk_import.py:631 msgid "Device bay" msgstr "" -#: netbox/dcim/forms/bulk_import.py:604 +#: dcim/forms/bulk_import.py:635 msgid "Device bay in which this device is installed (for child devices)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:671 +#: dcim/forms/bulk_import.py:702 msgid "The device in which this module is installed" msgstr "" -#: netbox/dcim/forms/bulk_import.py:674 netbox/dcim/forms/model_forms.py:651 +#: dcim/forms/bulk_import.py:705 dcim/forms/model_forms.py:745 msgid "Module bay" msgstr "" -#: netbox/dcim/forms/bulk_import.py:677 +#: dcim/forms/bulk_import.py:708 msgid "The module bay in which this module is installed" msgstr "" -#: netbox/dcim/forms/bulk_import.py:683 +#: dcim/forms/bulk_import.py:714 msgid "The type of module" msgstr "" -#: netbox/dcim/forms/bulk_import.py:691 netbox/dcim/forms/model_forms.py:667 +#: dcim/forms/bulk_import.py:722 dcim/forms/model_forms.py:761 msgid "Replicate components" msgstr "" -#: netbox/dcim/forms/bulk_import.py:693 +#: dcim/forms/bulk_import.py:724 msgid "" "Automatically populate components associated with this module type (enabled " "by default)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:696 netbox/dcim/forms/model_forms.py:673 +#: dcim/forms/bulk_import.py:727 dcim/forms/model_forms.py:767 msgid "Adopt components" msgstr "" -#: netbox/dcim/forms/bulk_import.py:698 netbox/dcim/forms/model_forms.py:676 +#: dcim/forms/bulk_import.py:729 dcim/forms/model_forms.py:770 msgid "Adopt already existing components" msgstr "" -#: netbox/dcim/forms/bulk_import.py:738 netbox/dcim/forms/bulk_import.py:764 -#: netbox/dcim/forms/bulk_import.py:790 +#: dcim/forms/bulk_import.py:769 dcim/forms/bulk_import.py:795 +#: dcim/forms/bulk_import.py:821 msgid "Port type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:746 netbox/dcim/forms/bulk_import.py:772 +#: dcim/forms/bulk_import.py:777 dcim/forms/bulk_import.py:803 msgid "Port speed in bps" msgstr "" -#: netbox/dcim/forms/bulk_import.py:810 +#: dcim/forms/bulk_import.py:841 msgid "Outlet type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:817 +#: dcim/forms/bulk_import.py:848 msgid "Local power port which feeds this outlet" msgstr "" -#: netbox/dcim/forms/bulk_import.py:823 +#: dcim/forms/bulk_import.py:854 msgid "Electrical phase (for three-phase circuits)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 -#: netbox/virtualization/forms/bulk_import.py:161 -#: netbox/virtualization/forms/model_forms.py:319 +#: dcim/forms/bulk_import.py:898 dcim/forms/model_forms.py:1464 +#: virtualization/forms/bulk_import.py:161 +#: virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 -#: netbox/virtualization/forms/bulk_import.py:168 -#: netbox/virtualization/forms/model_forms.py:327 +#: dcim/forms/bulk_import.py:905 dcim/forms/model_forms.py:1472 +#: virtualization/forms/bulk_import.py:168 +#: virtualization/forms/model_forms.py:327 msgid "Bridged interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:877 +#: dcim/forms/bulk_import.py:908 msgid "Lag" msgstr "" -#: netbox/dcim/forms/bulk_import.py:881 +#: dcim/forms/bulk_import.py:912 msgid "Parent LAG interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:884 +#: dcim/forms/bulk_import.py:915 msgid "Vdcs" msgstr "" -#: netbox/dcim/forms/bulk_import.py:889 +#: dcim/forms/bulk_import.py:920 msgid "VDC names separated by commas, encased with double quotes. Example:" msgstr "" -#: netbox/dcim/forms/bulk_import.py:895 +#: dcim/forms/bulk_import.py:926 msgid "Physical medium" msgstr "" -#: netbox/dcim/forms/bulk_import.py:898 netbox/dcim/forms/filtersets.py:1419 +#: dcim/forms/bulk_import.py:929 dcim/forms/filtersets.py:1443 msgid "Duplex" msgstr "" -#: netbox/dcim/forms/bulk_import.py:903 +#: dcim/forms/bulk_import.py:934 msgid "Poe mode" msgstr "" -#: netbox/dcim/forms/bulk_import.py:909 +#: dcim/forms/bulk_import.py:940 msgid "Poe type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:918 -#: netbox/virtualization/forms/bulk_import.py:174 +#: dcim/forms/bulk_import.py:949 virtualization/forms/bulk_import.py:174 msgid "IEEE 802.1Q operational mode (for L2 interfaces)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:925 netbox/ipam/forms/bulk_import.py:164 -#: netbox/ipam/forms/bulk_import.py:253 netbox/ipam/forms/bulk_import.py:289 -#: netbox/ipam/forms/filtersets.py:210 netbox/ipam/forms/filtersets.py:293 -#: netbox/ipam/forms/filtersets.py:353 -#: netbox/virtualization/forms/bulk_import.py:181 +#: dcim/forms/bulk_import.py:956 ipam/forms/bulk_import.py:164 +#: ipam/forms/bulk_import.py:253 ipam/forms/bulk_import.py:289 +#: ipam/forms/filtersets.py:210 ipam/forms/filtersets.py:293 +#: ipam/forms/filtersets.py:360 virtualization/forms/bulk_import.py:181 msgid "Assigned VRF" msgstr "" -#: netbox/dcim/forms/bulk_import.py:928 +#: dcim/forms/bulk_import.py:959 msgid "Rf role" msgstr "" -#: netbox/dcim/forms/bulk_import.py:931 +#: dcim/forms/bulk_import.py:962 msgid "Wireless role (AP/station)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:967 +#: dcim/forms/bulk_import.py:998 #, python-brace-format msgid "VDC {vdc} is not assigned to device {device}" msgstr "" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 -#: netbox/dcim/forms/model_forms.py:1655 netbox/dcim/forms/object_import.py:117 +#: dcim/forms/bulk_import.py:1012 dcim/forms/model_forms.py:1130 +#: dcim/forms/model_forms.py:1749 dcim/forms/object_import.py:117 msgid "Rear port" msgstr "" -#: netbox/dcim/forms/bulk_import.py:984 +#: dcim/forms/bulk_import.py:1015 msgid "Corresponding rear port" msgstr "" -#: netbox/dcim/forms/bulk_import.py:989 netbox/dcim/forms/bulk_import.py:1030 -#: netbox/dcim/forms/bulk_import.py:1353 +#: dcim/forms/bulk_import.py:1020 dcim/forms/bulk_import.py:1061 +#: dcim/forms/bulk_import.py:1384 msgid "Physical medium classification" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1058 netbox/dcim/tables/devices.py:853 +#: dcim/forms/bulk_import.py:1089 dcim/tables/devices.py:860 msgid "Installed device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1062 +#: dcim/forms/bulk_import.py:1093 msgid "Child device installed within this bay" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1064 +#: dcim/forms/bulk_import.py:1095 msgid "Child device not found." msgstr "" -#: netbox/dcim/forms/bulk_import.py:1122 +#: dcim/forms/bulk_import.py:1153 msgid "Parent inventory item" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1125 +#: dcim/forms/bulk_import.py:1156 msgid "Component type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1129 +#: dcim/forms/bulk_import.py:1160 msgid "Component Type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1132 +#: dcim/forms/bulk_import.py:1163 msgid "Compnent name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1134 +#: dcim/forms/bulk_import.py:1165 msgid "Component Name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1177 netbox/dcim/forms/bulk_import.py:1195 +#: dcim/forms/bulk_import.py:1208 dcim/forms/bulk_import.py:1226 msgid "Component name must be specified when component type is specified" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1187 +#: dcim/forms/bulk_import.py:1218 #, python-brace-format msgid "Component not found: {device} - {component_name}" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1200 +#: dcim/forms/bulk_import.py:1231 msgid "Component type must be specified when component name is specified" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1227 netbox/ipam/forms/bulk_import.py:314 +#: dcim/forms/bulk_import.py:1258 ipam/forms/bulk_import.py:314 msgid "Parent device of assigned interface (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1230 netbox/ipam/forms/bulk_import.py:317 -#: netbox/ipam/forms/bulk_import.py:563 netbox/ipam/forms/model_forms.py:767 -#: netbox/virtualization/filtersets.py:254 -#: netbox/virtualization/filtersets.py:305 -#: netbox/virtualization/forms/bulk_edit.py:182 -#: netbox/virtualization/forms/bulk_edit.py:316 -#: netbox/virtualization/forms/bulk_import.py:152 -#: netbox/virtualization/forms/bulk_import.py:213 -#: netbox/virtualization/forms/filtersets.py:220 -#: netbox/virtualization/forms/filtersets.py:266 -#: netbox/virtualization/forms/model_forms.py:295 -#: netbox/vpn/forms/bulk_import.py:93 netbox/vpn/forms/bulk_import.py:290 +#: dcim/forms/bulk_import.py:1261 ipam/forms/bulk_import.py:317 +#: virtualization/filtersets.py:256 virtualization/filtersets.py:307 +#: virtualization/forms/bulk_edit.py:182 virtualization/forms/bulk_edit.py:316 +#: virtualization/forms/bulk_import.py:152 +#: virtualization/forms/bulk_import.py:213 +#: virtualization/forms/filtersets.py:220 +#: virtualization/forms/filtersets.py:266 +#: virtualization/forms/model_forms.py:295 vpn/forms/bulk_import.py:93 +#: vpn/forms/bulk_import.py:295 msgid "Virtual machine" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1234 netbox/ipam/forms/bulk_import.py:321 +#: dcim/forms/bulk_import.py:1265 ipam/forms/bulk_import.py:321 msgid "Parent VM of assigned interface (if any)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1241 netbox/ipam/filtersets.py:1047 -#: netbox/ipam/forms/bulk_import.py:328 +#: dcim/forms/bulk_import.py:1272 ipam/filtersets.py:1047 +#: ipam/forms/bulk_import.py:328 msgid "Assigned interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1244 netbox/ipam/forms/bulk_import.py:338 +#: dcim/forms/bulk_import.py:1275 ipam/forms/bulk_import.py:338 msgid "Is primary" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1245 +#: dcim/forms/bulk_import.py:1276 msgid "Make this the primary MAC address for the assigned interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1282 +#: dcim/forms/bulk_import.py:1313 msgid "Must specify the parent device or VM when assigning an interface" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1308 +#: dcim/forms/bulk_import.py:1339 msgid "Side A device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1311 netbox/dcim/forms/bulk_import.py:1329 +#: dcim/forms/bulk_import.py:1342 dcim/forms/bulk_import.py:1360 msgid "Device name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1314 +#: dcim/forms/bulk_import.py:1345 msgid "Side A type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1320 +#: dcim/forms/bulk_import.py:1351 msgid "Side A name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1321 netbox/dcim/forms/bulk_import.py:1339 +#: dcim/forms/bulk_import.py:1352 dcim/forms/bulk_import.py:1370 msgid "Termination name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1326 +#: dcim/forms/bulk_import.py:1357 msgid "Side B device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1332 +#: dcim/forms/bulk_import.py:1363 msgid "Side B type" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1338 +#: dcim/forms/bulk_import.py:1369 msgid "Side B name" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1347 -#: netbox/wireless/forms/bulk_import.py:91 +#: dcim/forms/bulk_import.py:1378 wireless/forms/bulk_import.py:91 msgid "Connection status" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1399 +#: dcim/forms/bulk_import.py:1430 #, python-brace-format msgid "Side {side_upper}: {device} {termination_object} is already connected" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1405 +#: dcim/forms/bulk_import.py:1436 #, python-brace-format msgid "{side_upper} side termination not found: {device} {name}" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1430 netbox/dcim/forms/model_forms.py:797 -#: netbox/dcim/tables/devices.py:1058 netbox/templates/dcim/device.html:132 -#: netbox/templates/dcim/virtualchassis.html:27 -#: netbox/templates/dcim/virtualchassis.html:67 +#: dcim/forms/bulk_import.py:1461 dcim/forms/model_forms.py:891 +#: dcim/tables/devices.py:1065 templates/dcim/device.html:132 +#: templates/dcim/virtualchassis.html:27 templates/dcim/virtualchassis.html:67 msgid "Master" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1434 +#: dcim/forms/bulk_import.py:1465 msgid "Master device" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1451 +#: dcim/forms/bulk_import.py:1482 msgid "Name of parent site" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1485 +#: dcim/forms/bulk_import.py:1516 msgid "Upstream power panel" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1515 +#: dcim/forms/bulk_import.py:1546 msgid "Primary or redundant" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1520 +#: dcim/forms/bulk_import.py:1551 msgid "Supply type (AC/DC)" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1525 +#: dcim/forms/bulk_import.py:1556 msgid "Single or three-phase" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 -#: netbox/templates/dcim/device.html:190 -#: netbox/templates/dcim/virtualdevicecontext.html:30 -#: netbox/templates/virtualization/virtualmachine.html:52 +#: dcim/forms/bulk_import.py:1607 dcim/forms/model_forms.py:1847 +#: templates/dcim/device.html:190 templates/dcim/virtualdevicecontext.html:30 +#: templates/virtualization/virtualmachine.html:52 msgid "Primary IPv4" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1580 +#: dcim/forms/bulk_import.py:1611 msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 -#: netbox/templates/dcim/device.html:206 -#: netbox/templates/dcim/virtualdevicecontext.html:41 -#: netbox/templates/virtualization/virtualmachine.html:68 +#: dcim/forms/bulk_import.py:1614 dcim/forms/model_forms.py:1856 +#: templates/dcim/device.html:206 templates/dcim/virtualdevicecontext.html:41 +#: templates/virtualization/virtualmachine.html:68 msgid "Primary IPv6" msgstr "" -#: netbox/dcim/forms/bulk_import.py:1587 +#: dcim/forms/bulk_import.py:1618 msgid "IPv6 address with prefix length, e.g. 2001:db8::1/64" msgstr "" -#: netbox/dcim/forms/common.py:19 netbox/dcim/models/device_components.py:518 -#: netbox/templates/dcim/interface.html:57 -#: netbox/templates/virtualization/vminterface.html:51 -#: netbox/virtualization/forms/bulk_edit.py:207 +#: dcim/forms/common.py:19 dcim/models/device_components.py:527 +#: templates/dcim/interface.html:57 +#: templates/virtualization/vminterface.html:51 +#: virtualization/forms/bulk_edit.py:207 msgid "MTU" msgstr "" -#: netbox/dcim/forms/common.py:59 +#: dcim/forms/common.py:59 #, python-brace-format msgid "" "The tagged VLANs ({vlans}) must belong to the same site as the interface's " "parent device/VM, or they must be global" msgstr "" -#: netbox/dcim/forms/common.py:126 +#: dcim/forms/common.py:126 msgid "" "Cannot install module with placeholder values in a module bay with no " "position defined." msgstr "" -#: netbox/dcim/forms/common.py:132 +#: dcim/forms/common.py:132 #, python-brace-format msgid "" "Cannot install module with placeholder values in a module bay tree {level} " "in tree but {tokens} placeholders given." msgstr "" -#: netbox/dcim/forms/common.py:147 +#: dcim/forms/common.py:147 #, python-brace-format msgid "Cannot adopt {model} {name} as it already belongs to a module" msgstr "" -#: netbox/dcim/forms/common.py:156 +#: dcim/forms/common.py:156 #, python-brace-format msgid "A {model} named {name} already exists" msgstr "" -#: netbox/dcim/forms/connections.py:49 netbox/dcim/forms/model_forms.py:749 -#: netbox/dcim/tables/power.py:66 -#: netbox/templates/dcim/inc/cable_termination.html:42 -#: netbox/templates/dcim/powerfeed.html:24 -#: netbox/templates/dcim/powerpanel.html:19 -#: netbox/templates/dcim/trace/powerpanel.html:4 +#: dcim/forms/connections.py:49 dcim/forms/model_forms.py:843 +#: dcim/tables/power.py:66 templates/dcim/inc/cable_termination.html:42 +#: templates/dcim/powerfeed.html:24 templates/dcim/powerpanel.html:19 +#: templates/dcim/trace/powerpanel.html:4 msgid "Power Panel" msgstr "" -#: netbox/dcim/forms/connections.py:58 netbox/dcim/forms/model_forms.py:777 -#: netbox/templates/dcim/powerfeed.html:21 -#: netbox/templates/dcim/powerport.html:80 +#: dcim/forms/connections.py:58 dcim/forms/model_forms.py:871 +#: templates/dcim/powerfeed.html:21 templates/dcim/powerport.html:80 msgid "Power Feed" msgstr "" -#: netbox/dcim/forms/filtersets.py:137 netbox/dcim/tables/devices.py:304 +#: dcim/forms/filtersets.py:138 dcim/tables/devices.py:304 msgid "Device Status" msgstr "" -#: netbox/dcim/forms/filtersets.py:150 +#: dcim/forms/filtersets.py:151 msgid "Parent region" msgstr "" -#: netbox/dcim/forms/filtersets.py:164 netbox/tenancy/forms/bulk_import.py:28 -#: netbox/tenancy/forms/bulk_import.py:62 netbox/tenancy/forms/filtersets.py:33 -#: netbox/tenancy/forms/filtersets.py:62 -#: netbox/wireless/forms/bulk_import.py:27 -#: netbox/wireless/forms/filtersets.py:27 +#: dcim/forms/filtersets.py:165 tenancy/forms/bulk_import.py:28 +#: tenancy/forms/bulk_import.py:62 tenancy/forms/filtersets.py:33 +#: tenancy/forms/filtersets.py:62 wireless/forms/bulk_import.py:27 +#: wireless/forms/filtersets.py:27 msgid "Parent group" msgstr "" -#: netbox/dcim/forms/filtersets.py:243 netbox/templates/dcim/location.html:58 -#: netbox/templates/dcim/site.html:56 +#: dcim/forms/filtersets.py:244 templates/dcim/location.html:58 +#: templates/dcim/site.html:56 msgid "Facility" msgstr "" -#: netbox/dcim/forms/filtersets.py:398 +#: dcim/forms/filtersets.py:399 msgid "Function" msgstr "" -#: netbox/dcim/forms/filtersets.py:484 netbox/dcim/forms/model_forms.py:382 -#: netbox/templates/inc/panels/image_attachments.html:6 +#: dcim/forms/filtersets.py:485 dcim/forms/model_forms.py:390 +#: templates/inc/panels/image_attachments.html:6 msgid "Images" msgstr "" -#: netbox/dcim/forms/filtersets.py:487 netbox/dcim/forms/filtersets.py:612 -#: netbox/dcim/forms/filtersets.py:727 +#: dcim/forms/filtersets.py:488 dcim/forms/filtersets.py:621 +#: dcim/forms/filtersets.py:746 msgid "Components" msgstr "" -#: netbox/dcim/forms/filtersets.py:507 +#: dcim/forms/filtersets.py:508 msgid "Subdevice role" msgstr "" -#: netbox/dcim/forms/filtersets.py:791 netbox/dcim/tables/racks.py:54 -#: netbox/templates/dcim/racktype.html:20 +#: dcim/forms/filtersets.py:810 dcim/tables/racks.py:54 +#: templates/dcim/module.html:99 templates/dcim/racktype.html:20 msgid "Model" msgstr "" -#: netbox/dcim/forms/filtersets.py:835 +#: dcim/forms/filtersets.py:854 msgid "Has an OOB IP" msgstr "" -#: netbox/dcim/forms/filtersets.py:842 +#: dcim/forms/filtersets.py:861 msgid "Virtual chassis member" msgstr "" -#: netbox/dcim/forms/filtersets.py:891 +#: dcim/forms/filtersets.py:910 msgid "Has virtual device contexts" msgstr "" -#: netbox/dcim/forms/filtersets.py:904 netbox/extras/filtersets.py:585 -#: netbox/ipam/forms/filtersets.py:469 -#: netbox/virtualization/forms/filtersets.py:118 +#: dcim/forms/filtersets.py:923 extras/filtersets.py:678 +#: ipam/forms/filtersets.py:477 virtualization/forms/filtersets.py:118 msgid "Cluster group" msgstr "" -#: netbox/dcim/forms/filtersets.py:1259 +#: dcim/forms/filtersets.py:1278 msgid "Cabled" msgstr "" -#: netbox/dcim/forms/filtersets.py:1266 +#: dcim/forms/filtersets.py:1285 msgid "Occupied" msgstr "" -#: netbox/dcim/forms/filtersets.py:1293 netbox/dcim/forms/filtersets.py:1318 -#: netbox/dcim/forms/filtersets.py:1342 netbox/dcim/forms/filtersets.py:1362 -#: netbox/dcim/forms/filtersets.py:1390 netbox/dcim/tables/devices.py:373 -#: netbox/dcim/tables/devices.py:662 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:16 -#: netbox/templates/dcim/consoleport.html:55 -#: netbox/templates/dcim/consoleserverport.html:55 -#: netbox/templates/dcim/frontport.html:69 -#: netbox/templates/dcim/interface.html:197 -#: netbox/templates/dcim/powerfeed.html:110 -#: netbox/templates/dcim/poweroutlet.html:69 -#: netbox/templates/dcim/powerport.html:59 -#: netbox/templates/dcim/rearport.html:65 +#: dcim/forms/filtersets.py:1312 dcim/forms/filtersets.py:1337 +#: dcim/forms/filtersets.py:1361 dcim/forms/filtersets.py:1381 +#: dcim/forms/filtersets.py:1414 dcim/tables/devices.py:373 +#: dcim/tables/devices.py:669 +#: templates/circuits/inc/circuit_termination_fields.html:16 +#: templates/dcim/consoleport.html:55 templates/dcim/consoleserverport.html:55 +#: templates/dcim/frontport.html:69 templates/dcim/interface.html:197 +#: templates/dcim/powerfeed.html:110 templates/dcim/poweroutlet.html:73 +#: templates/dcim/powerport.html:59 templates/dcim/rearport.html:65 msgid "Connection" msgstr "" -#: netbox/dcim/forms/filtersets.py:1402 netbox/extras/forms/bulk_edit.py:326 -#: netbox/extras/forms/bulk_import.py:247 netbox/extras/forms/filtersets.py:472 -#: netbox/extras/forms/model_forms.py:689 netbox/extras/tables/tables.py:585 -#: netbox/templates/extras/journalentry.html:30 +#: dcim/forms/filtersets.py:1426 extras/forms/bulk_edit.py:382 +#: extras/forms/bulk_import.py:253 extras/forms/filtersets.py:527 +#: extras/forms/model_forms.py:759 extras/tables/tables.py:640 +#: templates/extras/journalentry.html:30 msgid "Kind" msgstr "" -#: netbox/dcim/forms/filtersets.py:1431 +#: dcim/forms/filtersets.py:1455 msgid "Mgmt only" msgstr "" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 -#: netbox/dcim/models/device_components.py:680 -#: netbox/templates/dcim/interface.html:142 +#: dcim/forms/filtersets.py:1467 dcim/forms/model_forms.py:1548 +#: dcim/models/device_components.py:689 templates/dcim/interface.html:142 msgid "WWN" msgstr "" -#: netbox/dcim/forms/filtersets.py:1458 -#: netbox/virtualization/forms/filtersets.py:246 +#: dcim/forms/filtersets.py:1482 virtualization/forms/filtersets.py:246 msgid "802.1Q mode" msgstr "" -#: netbox/dcim/forms/filtersets.py:1473 +#: dcim/forms/filtersets.py:1497 msgid "Wireless channel" msgstr "" -#: netbox/dcim/forms/filtersets.py:1477 +#: dcim/forms/filtersets.py:1501 msgid "Channel frequency (MHz)" msgstr "" -#: netbox/dcim/forms/filtersets.py:1481 +#: dcim/forms/filtersets.py:1505 msgid "Channel width (MHz)" msgstr "" -#: netbox/dcim/forms/filtersets.py:1485 netbox/templates/dcim/interface.html:91 +#: dcim/forms/filtersets.py:1509 templates/dcim/interface.html:91 msgid "Transmit power (dBm)" msgstr "" -#: netbox/dcim/forms/filtersets.py:1510 netbox/dcim/forms/filtersets.py:1535 -#: netbox/dcim/tables/devices.py:336 netbox/templates/dcim/cable.html:12 -#: netbox/templates/dcim/cable_trace.html:46 -#: netbox/templates/dcim/frontport.html:77 -#: netbox/templates/dcim/htmx/cable_edit.html:53 -#: netbox/templates/dcim/inc/connection_endpoints.html:4 -#: netbox/templates/dcim/rearport.html:73 -#: netbox/templates/dcim/trace/cable.html:7 +#: dcim/forms/filtersets.py:1534 dcim/forms/filtersets.py:1559 +#: dcim/tables/devices.py:336 templates/dcim/cable.html:12 +#: templates/dcim/cable_trace.html:46 templates/dcim/frontport.html:77 +#: templates/dcim/htmx/cable_edit.html:53 +#: templates/dcim/inc/connection_endpoints.html:4 +#: templates/dcim/rearport.html:73 templates/dcim/trace/cable.html:7 msgid "Cable" msgstr "" -#: netbox/dcim/forms/filtersets.py:1614 netbox/dcim/tables/devices.py:978 +#: dcim/forms/filtersets.py:1638 dcim/tables/devices.py:985 msgid "Discovered" msgstr "" -#: netbox/dcim/forms/filtersets.py:1655 netbox/ipam/forms/filtersets.py:364 +#: dcim/forms/filtersets.py:1679 ipam/forms/filtersets.py:371 msgid "Assigned Device" msgstr "" -#: netbox/dcim/forms/filtersets.py:1660 netbox/ipam/forms/filtersets.py:369 +#: dcim/forms/filtersets.py:1684 ipam/forms/filtersets.py:376 msgid "Assigned VM" msgstr "" -#: netbox/dcim/forms/formsets.py:20 +#: dcim/forms/formsets.py:20 #, python-brace-format msgid "A virtual chassis member already exists in position {vc_position}." msgstr "" -#: netbox/dcim/forms/mixins.py:27 netbox/dcim/forms/mixins.py:75 -#: netbox/ipam/forms/bulk_edit.py:420 netbox/ipam/forms/model_forms.py:617 +#: dcim/forms/mixins.py:27 dcim/forms/mixins.py:75 ipam/forms/bulk_edit.py:425 +#: ipam/forms/model_forms.py:617 msgid "Scope type" msgstr "" -#: netbox/dcim/forms/mixins.py:30 netbox/dcim/forms/mixins.py:78 -#: netbox/ipam/forms/bulk_edit.py:270 netbox/ipam/forms/bulk_edit.py:423 -#: netbox/ipam/forms/bulk_edit.py:437 netbox/ipam/forms/filtersets.py:181 -#: netbox/ipam/forms/model_forms.py:231 netbox/ipam/forms/model_forms.py:620 -#: netbox/ipam/forms/model_forms.py:630 netbox/ipam/tables/ip.py:194 -#: netbox/ipam/tables/vlans.py:40 netbox/templates/ipam/prefix.html:48 -#: netbox/templates/ipam/vlangroup.html:38 -#: netbox/templates/virtualization/cluster.html:42 -#: netbox/templates/wireless/wirelesslan.html:26 -#: netbox/virtualization/forms/bulk_edit.py:91 -#: netbox/virtualization/forms/filtersets.py:47 -#: netbox/virtualization/forms/model_forms.py:79 -#: netbox/virtualization/tables/clusters.py:80 -#: netbox/wireless/forms/bulk_edit.py:93 netbox/wireless/forms/filtersets.py:37 -#: netbox/wireless/forms/model_forms.py:56 -#: netbox/wireless/tables/wirelesslan.py:58 +#: dcim/forms/mixins.py:30 dcim/forms/mixins.py:78 ipam/forms/bulk_edit.py:270 +#: ipam/forms/bulk_edit.py:428 ipam/forms/bulk_edit.py:447 +#: ipam/forms/filtersets.py:181 ipam/forms/model_forms.py:231 +#: ipam/forms/model_forms.py:620 ipam/forms/model_forms.py:630 +#: ipam/tables/ip.py:195 ipam/tables/vlans.py:40 templates/ipam/prefix.html:48 +#: templates/ipam/vlangroup.html:38 templates/virtualization/cluster.html:42 +#: templates/wireless/wirelesslan.html:26 virtualization/forms/bulk_edit.py:91 +#: virtualization/forms/filtersets.py:47 virtualization/forms/model_forms.py:79 +#: virtualization/tables/clusters.py:80 wireless/forms/bulk_edit.py:94 +#: wireless/forms/filtersets.py:37 wireless/forms/model_forms.py:57 +#: wireless/tables/wirelesslan.py:58 msgid "Scope" msgstr "" -#: netbox/dcim/forms/mixins.py:104 netbox/ipam/forms/bulk_import.py:452 +#: dcim/forms/mixins.py:104 ipam/forms/bulk_import.py:452 msgid "Scope type (app & model)" msgstr "" -#: netbox/dcim/forms/model_forms.py:144 +#: dcim/forms/model_forms.py:149 msgid "Contact Info" msgstr "" -#: netbox/dcim/forms/model_forms.py:199 netbox/templates/dcim/rackrole.html:19 +#: dcim/forms/model_forms.py:206 templates/dcim/rackrole.html:19 msgid "Rack Role" msgstr "" -#: netbox/dcim/forms/model_forms.py:217 netbox/dcim/forms/model_forms.py:371 -#: netbox/dcim/forms/model_forms.py:456 -#: netbox/utilities/forms/fields/fields.py:47 +#: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:379 +#: dcim/forms/model_forms.py:550 utilities/forms/fields/fields.py:47 msgid "Slug" msgstr "" -#: netbox/dcim/forms/model_forms.py:264 +#: dcim/forms/model_forms.py:271 msgid "Select a pre-defined rack type, or set physical characteristics below." msgstr "" -#: netbox/dcim/forms/model_forms.py:273 +#: dcim/forms/model_forms.py:280 msgid "Inventory Control" msgstr "" -#: netbox/dcim/forms/model_forms.py:321 +#: dcim/forms/model_forms.py:329 msgid "" "Comma-separated list of numeric unit IDs. A range may be specified using a " "hyphen." msgstr "" -#: netbox/dcim/forms/model_forms.py:330 netbox/dcim/tables/racks.py:201 +#: dcim/forms/model_forms.py:338 dcim/tables/racks.py:210 msgid "Reservation" msgstr "" -#: netbox/dcim/forms/model_forms.py:432 -#: netbox/templates/dcim/devicerole.html:23 +#: dcim/forms/model_forms.py:414 +msgid "Enter a valid JSON schema to define supported attributes." +msgstr "" + +#: dcim/forms/model_forms.py:447 +msgid "Profile & Attributes" +msgstr "" + +#: dcim/forms/model_forms.py:526 templates/dcim/devicerole.html:23 msgid "Device Role" msgstr "" -#: netbox/dcim/forms/model_forms.py:500 netbox/dcim/models/devices.py:635 +#: dcim/forms/model_forms.py:594 dcim/models/devices.py:523 msgid "The lowest-numbered unit occupied by the device" msgstr "" -#: netbox/dcim/forms/model_forms.py:558 +#: dcim/forms/model_forms.py:652 msgid "The position in the virtual chassis this device is identified by" msgstr "" -#: netbox/dcim/forms/model_forms.py:563 +#: dcim/forms/model_forms.py:657 msgid "The priority of the device in the virtual chassis" msgstr "" -#: netbox/dcim/forms/model_forms.py:670 +#: dcim/forms/model_forms.py:764 msgid "Automatically populate components associated with this module type" msgstr "" -#: netbox/dcim/forms/model_forms.py:779 +#: dcim/forms/model_forms.py:873 msgid "Characteristics" msgstr "" -#: netbox/dcim/forms/model_forms.py:936 +#: dcim/forms/model_forms.py:1030 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5334,3350 +4953,3245 @@ msgid "" "replaced with the position value when creating a new module." msgstr "" -#: netbox/dcim/forms/model_forms.py:1138 +#: dcim/forms/model_forms.py:1232 msgid "Console port template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1146 +#: dcim/forms/model_forms.py:1240 msgid "Console server port template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1154 +#: dcim/forms/model_forms.py:1248 msgid "Front port template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1162 +#: dcim/forms/model_forms.py:1256 msgid "Interface template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1170 +#: dcim/forms/model_forms.py:1264 msgid "Power outlet template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1178 +#: dcim/forms/model_forms.py:1272 msgid "Power port template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1186 +#: dcim/forms/model_forms.py:1280 msgid "Rear port template" msgstr "" -#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 -#: netbox/dcim/tables/connections.py:27 -#: netbox/templates/dcim/consoleport.html:17 -#: netbox/templates/dcim/consoleserverport.html:74 -#: netbox/templates/dcim/frontport.html:112 +#: dcim/forms/model_forms.py:1290 dcim/forms/model_forms.py:1761 +#: dcim/tables/connections.py:27 templates/dcim/consoleport.html:17 +#: templates/dcim/consoleserverport.html:74 templates/dcim/frontport.html:112 msgid "Console Port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 -#: netbox/templates/dcim/consoleport.html:73 -#: netbox/templates/dcim/consoleserverport.html:17 -#: netbox/templates/dcim/frontport.html:109 +#: dcim/forms/model_forms.py:1291 dcim/forms/model_forms.py:1762 +#: templates/dcim/consoleport.html:73 templates/dcim/consoleserverport.html:17 +#: templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:53 -#: netbox/templates/dcim/consoleport.html:76 -#: netbox/templates/dcim/consoleserverport.html:77 -#: netbox/templates/dcim/frontport.html:17 -#: netbox/templates/dcim/frontport.html:115 -#: netbox/templates/dcim/interface.html:244 -#: netbox/templates/dcim/rearport.html:105 +#: dcim/forms/model_forms.py:1292 dcim/forms/model_forms.py:1763 +#: templates/circuits/inc/circuit_termination_fields.html:53 +#: templates/dcim/consoleport.html:76 templates/dcim/consoleserverport.html:77 +#: templates/dcim/frontport.html:17 templates/dcim/frontport.html:115 +#: templates/dcim/interface.html:244 templates/dcim/rearport.html:105 msgid "Front Port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 -#: netbox/dcim/tables/devices.py:743 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:54 -#: netbox/templates/dcim/consoleport.html:79 -#: netbox/templates/dcim/consoleserverport.html:80 -#: netbox/templates/dcim/frontport.html:50 -#: netbox/templates/dcim/frontport.html:118 -#: netbox/templates/dcim/interface.html:247 -#: netbox/templates/dcim/rearport.html:17 -#: netbox/templates/dcim/rearport.html:108 +#: dcim/forms/model_forms.py:1293 dcim/forms/model_forms.py:1764 +#: dcim/tables/devices.py:750 +#: templates/circuits/inc/circuit_termination_fields.html:54 +#: templates/dcim/consoleport.html:79 templates/dcim/consoleserverport.html:80 +#: templates/dcim/frontport.html:50 templates/dcim/frontport.html:118 +#: templates/dcim/interface.html:247 templates/dcim/rearport.html:17 +#: templates/dcim/rearport.html:108 msgid "Rear Port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 -#: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 -#: netbox/templates/dcim/poweroutlet.html:54 -#: netbox/templates/dcim/powerport.html:17 +#: dcim/forms/model_forms.py:1294 dcim/forms/model_forms.py:1765 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:520 +#: templates/dcim/poweroutlet.html:58 templates/dcim/powerport.html:17 msgid "Power Port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 -#: netbox/templates/dcim/poweroutlet.html:17 -#: netbox/templates/dcim/powerport.html:77 +#: dcim/forms/model_forms.py:1295 dcim/forms/model_forms.py:1766 +#: templates/dcim/poweroutlet.html:17 templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "" -#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 +#: dcim/forms/model_forms.py:1297 dcim/forms/model_forms.py:1768 msgid "Component Assignment" msgstr "" -#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 +#: dcim/forms/model_forms.py:1343 dcim/forms/model_forms.py:1815 msgid "An InventoryItem can only be assigned to a single component." msgstr "" -#: netbox/dcim/forms/model_forms.py:1386 +#: dcim/forms/model_forms.py:1480 msgid "LAG interface" msgstr "" -#: netbox/dcim/forms/model_forms.py:1409 +#: dcim/forms/model_forms.py:1503 msgid "Filter VLANs available for assignment by group." msgstr "" -#: netbox/dcim/forms/model_forms.py:1564 +#: dcim/forms/model_forms.py:1658 msgid "Child Device" msgstr "" -#: netbox/dcim/forms/model_forms.py:1565 +#: dcim/forms/model_forms.py:1659 msgid "" "Child devices must first be created and assigned to the site and rack of the " "parent device." msgstr "" -#: netbox/dcim/forms/model_forms.py:1607 +#: dcim/forms/model_forms.py:1701 msgid "Console port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1615 +#: dcim/forms/model_forms.py:1709 msgid "Console server port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1623 +#: dcim/forms/model_forms.py:1717 msgid "Front port" msgstr "" -#: netbox/dcim/forms/model_forms.py:1639 +#: dcim/forms/model_forms.py:1733 msgid "Power outlet" msgstr "" -#: netbox/dcim/forms/model_forms.py:1661 -#: netbox/templates/dcim/inventoryitem.html:17 +#: dcim/forms/model_forms.py:1755 templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "" -#: netbox/dcim/forms/model_forms.py:1735 -#: netbox/templates/dcim/inventoryitemrole.html:15 +#: dcim/forms/model_forms.py:1829 templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "" -#: netbox/dcim/forms/model_forms.py:1804 +#: dcim/forms/model_forms.py:1898 msgid "VM Interface" msgstr "" -#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 -#: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 -#: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 -#: netbox/templates/virtualization/virtualdisk.html:21 -#: netbox/templates/virtualization/virtualmachine.html:12 -#: netbox/templates/virtualization/vminterface.html:21 -#: netbox/templates/vpn/tunneltermination.html:25 -#: netbox/virtualization/forms/filtersets.py:203 -#: netbox/virtualization/forms/filtersets.py:260 -#: netbox/virtualization/forms/model_forms.py:227 -#: netbox/virtualization/tables/virtualmachines.py:105 -#: netbox/virtualization/tables/virtualmachines.py:161 netbox/vpn/choices.py:53 -#: netbox/vpn/forms/filtersets.py:299 netbox/vpn/forms/model_forms.py:161 -#: netbox/vpn/forms/model_forms.py:172 netbox/vpn/forms/model_forms.py:274 -#: netbox/vpn/forms/model_forms.py:457 +#: dcim/forms/model_forms.py:1913 ipam/forms/filtersets.py:631 +#: ipam/forms/model_forms.py:334 ipam/tables/vlans.py:173 +#: templates/virtualization/virtualdisk.html:21 +#: templates/virtualization/virtualmachine.html:12 +#: templates/virtualization/vminterface.html:21 +#: templates/vpn/tunneltermination.html:25 +#: virtualization/forms/filtersets.py:203 +#: virtualization/forms/filtersets.py:260 +#: virtualization/forms/model_forms.py:227 +#: virtualization/tables/virtualmachines.py:105 +#: virtualization/tables/virtualmachines.py:161 vpn/choices.py:53 +#: vpn/forms/filtersets.py:304 vpn/forms/model_forms.py:161 +#: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 +#: vpn/forms/model_forms.py:457 msgid "Virtual Machine" msgstr "" -#: netbox/dcim/forms/model_forms.py:1858 +#: dcim/forms/model_forms.py:1952 msgid "A MAC address can only be assigned to a single object." msgstr "" -#: netbox/dcim/forms/object_create.py:48 netbox/dcim/forms/object_create.py:210 -#: netbox/dcim/forms/object_create.py:359 +#: dcim/forms/object_create.py:48 dcim/forms/object_create.py:210 +#: dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" msgstr "" -#: netbox/dcim/forms/object_create.py:72 +#: dcim/forms/object_create.py:72 #, python-brace-format msgid "" "The provided pattern specifies {value_count} values, but {pattern_count} are " "expected." msgstr "" -#: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 +#: dcim/forms/object_create.py:114 dcim/forms/object_create.py:274 +#: dcim/tables/devices.py:262 msgid "Rear ports" msgstr "" -#: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:275 +#: dcim/forms/object_create.py:115 dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" -#: netbox/dcim/forms/object_create.py:175 +#: dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " "match the selected number of rear port positions ({rearport_count})." msgstr "" -#: netbox/dcim/forms/object_create.py:324 +#: dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " "selected number of rear port positions ({rearport_count})." msgstr "" -#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 -#: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 -#: netbox/templates/dcim/virtualchassis_edit.html:51 -#: netbox/templates/ipam/fhrpgroup.html:38 +#: dcim/forms/object_create.py:413 dcim/tables/devices.py:1071 +#: ipam/tables/fhrp.py:31 templates/dcim/virtualchassis.html:53 +#: templates/dcim/virtualchassis_edit.html:51 templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "" -#: netbox/dcim/forms/object_create.py:423 +#: dcim/forms/object_create.py:423 msgid "Initial position" msgstr "" -#: netbox/dcim/forms/object_create.py:426 +#: dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." msgstr "" -#: netbox/dcim/forms/object_create.py:441 +#: dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "" -#: netbox/dcim/models/cables.py:62 -#: netbox/dcim/models/device_component_templates.py:51 -#: netbox/dcim/models/device_components.py:57 -#: netbox/extras/models/customfields.py:113 +#: dcim/models/cables.py:62 dcim/models/device_component_templates.py:51 +#: dcim/models/device_components.py:57 extras/models/customfields.py:113 msgid "label" msgstr "" -#: netbox/dcim/models/cables.py:71 +#: dcim/models/cables.py:71 msgid "length" msgstr "" -#: netbox/dcim/models/cables.py:78 +#: dcim/models/cables.py:78 msgid "length unit" msgstr "" -#: netbox/dcim/models/cables.py:96 +#: dcim/models/cables.py:96 msgid "cable" msgstr "" -#: netbox/dcim/models/cables.py:97 +#: dcim/models/cables.py:97 msgid "cables" msgstr "" -#: netbox/dcim/models/cables.py:163 +#: dcim/models/cables.py:163 msgid "Must specify a unit when setting a cable length" msgstr "" -#: netbox/dcim/models/cables.py:166 +#: dcim/models/cables.py:166 msgid "Must define A and B terminations when creating a new cable." msgstr "" -#: netbox/dcim/models/cables.py:173 +#: dcim/models/cables.py:173 msgid "Cannot connect different termination types to same end of cable." msgstr "" -#: netbox/dcim/models/cables.py:181 +#: dcim/models/cables.py:181 #, python-brace-format msgid "Incompatible termination types: {type_a} and {type_b}" msgstr "" -#: netbox/dcim/models/cables.py:191 +#: dcim/models/cables.py:191 msgid "A and B terminations cannot connect to the same object." msgstr "" -#: netbox/dcim/models/cables.py:260 netbox/ipam/models/asns.py:37 +#: dcim/models/cables.py:260 ipam/models/asns.py:37 msgid "end" msgstr "" -#: netbox/dcim/models/cables.py:313 +#: dcim/models/cables.py:309 msgid "cable termination" msgstr "" -#: netbox/dcim/models/cables.py:314 +#: dcim/models/cables.py:310 msgid "cable terminations" msgstr "" -#: netbox/dcim/models/cables.py:333 +#: dcim/models/cables.py:329 #, python-brace-format msgid "" "Duplicate termination found for {app_label}.{model} {termination_id}: cable " "{cable_pk}" msgstr "" -#: netbox/dcim/models/cables.py:343 +#: dcim/models/cables.py:339 #, python-brace-format msgid "Cables cannot be terminated to {type_display} interfaces" msgstr "" -#: netbox/dcim/models/cables.py:350 +#: dcim/models/cables.py:346 msgid "Circuit terminations attached to a provider network may not be cabled." msgstr "" -#: netbox/dcim/models/cables.py:448 netbox/extras/models/configs.py:50 +#: dcim/models/cables.py:444 extras/models/configs.py:47 msgid "is active" msgstr "" -#: netbox/dcim/models/cables.py:452 +#: dcim/models/cables.py:448 msgid "is complete" msgstr "" -#: netbox/dcim/models/cables.py:456 +#: dcim/models/cables.py:452 msgid "is split" msgstr "" -#: netbox/dcim/models/cables.py:464 +#: dcim/models/cables.py:460 msgid "cable path" msgstr "" -#: netbox/dcim/models/cables.py:465 +#: dcim/models/cables.py:461 msgid "cable paths" msgstr "" -#: netbox/dcim/models/cables.py:540 +#: dcim/models/cables.py:536 msgid "All originating terminations must be attached to the same link" msgstr "" -#: netbox/dcim/models/cables.py:552 +#: dcim/models/cables.py:548 msgid "All mid-span terminations must have the same termination type" msgstr "" -#: netbox/dcim/models/cables.py:557 +#: dcim/models/cables.py:553 msgid "All mid-span terminations must have the same parent object" msgstr "" -#: netbox/dcim/models/cables.py:581 +#: dcim/models/cables.py:577 msgid "All links must be cable or wireless" msgstr "" -#: netbox/dcim/models/cables.py:583 +#: dcim/models/cables.py:579 msgid "All links must match first link type" msgstr "" -#: netbox/dcim/models/cables.py:666 +#: dcim/models/cables.py:662 msgid "" "All positions counts within the path on opposite ends of links must match" msgstr "" -#: netbox/dcim/models/cables.py:675 +#: dcim/models/cables.py:671 msgid "Remote termination position filter is missing" msgstr "" -#: netbox/dcim/models/device_component_templates.py:46 +#: dcim/models/device_component_templates.py:46 #, python-brace-format msgid "" "{module} is accepted as a substitution for the module bay position when " "attached to a module type." msgstr "" -#: netbox/dcim/models/device_component_templates.py:54 -#: netbox/dcim/models/device_components.py:60 +#: dcim/models/device_component_templates.py:54 +#: dcim/models/device_components.py:60 msgid "Physical label" msgstr "" -#: netbox/dcim/models/device_component_templates.py:99 +#: dcim/models/device_component_templates.py:99 msgid "Component templates cannot be moved to a different device type." msgstr "" -#: netbox/dcim/models/device_component_templates.py:150 +#: dcim/models/device_component_templates.py:150 msgid "" "A component template cannot be associated with both a device type and a " "module type." msgstr "" -#: netbox/dcim/models/device_component_templates.py:154 +#: dcim/models/device_component_templates.py:154 msgid "" "A component template must be associated with either a device type or a " "module type." msgstr "" -#: netbox/dcim/models/device_component_templates.py:209 +#: dcim/models/device_component_templates.py:209 msgid "console port template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:210 +#: dcim/models/device_component_templates.py:210 msgid "console port templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:244 +#: dcim/models/device_component_templates.py:244 msgid "console server port template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:245 +#: dcim/models/device_component_templates.py:245 msgid "console server port templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:277 -#: netbox/dcim/models/device_components.py:348 +#: dcim/models/device_component_templates.py:277 +#: dcim/models/device_components.py:348 msgid "maximum draw" msgstr "" -#: netbox/dcim/models/device_component_templates.py:284 -#: netbox/dcim/models/device_components.py:355 +#: dcim/models/device_component_templates.py:284 +#: dcim/models/device_components.py:355 msgid "allocated draw" msgstr "" -#: netbox/dcim/models/device_component_templates.py:294 +#: dcim/models/device_component_templates.py:294 msgid "power port template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:295 +#: dcim/models/device_component_templates.py:295 msgid "power port templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:315 -#: netbox/dcim/models/device_components.py:375 +#: dcim/models/device_component_templates.py:315 +#: dcim/models/device_components.py:375 #, python-brace-format msgid "Allocated draw cannot exceed the maximum draw ({maximum_draw}W)." msgstr "" -#: netbox/dcim/models/device_component_templates.py:349 -#: netbox/dcim/models/device_components.py:471 +#: dcim/models/device_component_templates.py:349 +#: dcim/models/device_components.py:477 msgid "feed leg" msgstr "" -#: netbox/dcim/models/device_component_templates.py:354 -#: netbox/dcim/models/device_components.py:476 +#: dcim/models/device_component_templates.py:354 +#: dcim/models/device_components.py:482 msgid "Phase (for three-phase feeds)" msgstr "" -#: netbox/dcim/models/device_component_templates.py:360 +#: dcim/models/device_component_templates.py:360 msgid "power outlet template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:361 +#: dcim/models/device_component_templates.py:361 msgid "power outlet templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:370 +#: dcim/models/device_component_templates.py:370 #, python-brace-format msgid "Parent power port ({power_port}) must belong to the same device type" msgstr "" -#: netbox/dcim/models/device_component_templates.py:376 +#: dcim/models/device_component_templates.py:376 #, python-brace-format msgid "Parent power port ({power_port}) must belong to the same module type" msgstr "" -#: netbox/dcim/models/device_component_templates.py:430 -#: netbox/dcim/models/device_components.py:662 +#: dcim/models/device_component_templates.py:430 +#: dcim/models/device_components.py:671 msgid "management only" msgstr "" -#: netbox/dcim/models/device_component_templates.py:438 -#: netbox/dcim/models/device_components.py:542 +#: dcim/models/device_component_templates.py:438 +#: dcim/models/device_components.py:551 msgid "bridge interface" msgstr "" -#: netbox/dcim/models/device_component_templates.py:459 -#: netbox/dcim/models/device_components.py:688 +#: dcim/models/device_component_templates.py:459 +#: dcim/models/device_components.py:697 msgid "wireless role" msgstr "" -#: netbox/dcim/models/device_component_templates.py:465 +#: dcim/models/device_component_templates.py:465 msgid "interface template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:466 +#: dcim/models/device_component_templates.py:466 msgid "interface templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:473 -#: netbox/dcim/models/device_components.py:848 -#: netbox/virtualization/models/virtualmachines.py:385 +#: dcim/models/device_component_templates.py:473 +#: dcim/models/device_components.py:857 +#: virtualization/models/virtualmachines.py:390 msgid "An interface cannot be bridged to itself." msgstr "" -#: netbox/dcim/models/device_component_templates.py:477 +#: dcim/models/device_component_templates.py:477 #, python-brace-format msgid "Bridge interface ({bridge}) must belong to the same device type" msgstr "" -#: netbox/dcim/models/device_component_templates.py:483 +#: dcim/models/device_component_templates.py:483 #, python-brace-format msgid "Bridge interface ({bridge}) must belong to the same module type" msgstr "" -#: netbox/dcim/models/device_component_templates.py:540 -#: netbox/dcim/models/device_components.py:1038 +#: dcim/models/device_component_templates.py:540 +#: dcim/models/device_components.py:1047 msgid "rear port position" msgstr "" -#: netbox/dcim/models/device_component_templates.py:565 +#: dcim/models/device_component_templates.py:565 msgid "front port template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:566 +#: dcim/models/device_component_templates.py:566 msgid "front port templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:576 +#: dcim/models/device_component_templates.py:576 #, python-brace-format msgid "Rear port ({name}) must belong to the same device type" msgstr "" -#: netbox/dcim/models/device_component_templates.py:582 +#: dcim/models/device_component_templates.py:582 #, python-brace-format msgid "" "Invalid rear port position ({position}); rear port {name} has only {count} " "positions" msgstr "" -#: netbox/dcim/models/device_component_templates.py:635 -#: netbox/dcim/models/device_components.py:1104 +#: dcim/models/device_component_templates.py:635 +#: dcim/models/device_components.py:1113 msgid "positions" msgstr "" -#: netbox/dcim/models/device_component_templates.py:646 +#: dcim/models/device_component_templates.py:646 msgid "rear port template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:647 +#: dcim/models/device_component_templates.py:647 msgid "rear port templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:676 -#: netbox/dcim/models/device_components.py:1151 +#: dcim/models/device_component_templates.py:676 +#: dcim/models/device_components.py:1160 msgid "position" msgstr "" -#: netbox/dcim/models/device_component_templates.py:679 -#: netbox/dcim/models/device_components.py:1154 +#: dcim/models/device_component_templates.py:679 +#: dcim/models/device_components.py:1163 msgid "Identifier to reference when renaming installed components" msgstr "" -#: netbox/dcim/models/device_component_templates.py:685 +#: dcim/models/device_component_templates.py:685 msgid "module bay template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:686 +#: dcim/models/device_component_templates.py:686 msgid "module bay templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:713 +#: dcim/models/device_component_templates.py:713 msgid "device bay template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:714 +#: dcim/models/device_component_templates.py:714 msgid "device bay templates" msgstr "" -#: netbox/dcim/models/device_component_templates.py:728 +#: dcim/models/device_component_templates.py:728 #, python-brace-format msgid "" "Subdevice role of device type ({device_type}) must be set to \"parent\" to " "allow device bays." msgstr "" -#: netbox/dcim/models/device_component_templates.py:784 -#: netbox/dcim/models/device_components.py:1307 +#: dcim/models/device_component_templates.py:783 +#: dcim/models/device_components.py:1315 msgid "part ID" msgstr "" -#: netbox/dcim/models/device_component_templates.py:786 -#: netbox/dcim/models/device_components.py:1309 +#: dcim/models/device_component_templates.py:785 +#: dcim/models/device_components.py:1317 msgid "Manufacturer-assigned part identifier" msgstr "" -#: netbox/dcim/models/device_component_templates.py:803 +#: dcim/models/device_component_templates.py:802 msgid "inventory item template" msgstr "" -#: netbox/dcim/models/device_component_templates.py:804 +#: dcim/models/device_component_templates.py:803 msgid "inventory item templates" msgstr "" -#: netbox/dcim/models/device_components.py:100 +#: dcim/models/device_components.py:100 msgid "Components cannot be moved to a different device." msgstr "" -#: netbox/dcim/models/device_components.py:139 +#: dcim/models/device_components.py:139 msgid "cable end" msgstr "" -#: netbox/dcim/models/device_components.py:146 +#: dcim/models/device_components.py:146 msgid "mark connected" msgstr "" -#: netbox/dcim/models/device_components.py:148 +#: dcim/models/device_components.py:148 msgid "Treat as if a cable is connected" msgstr "" -#: netbox/dcim/models/device_components.py:166 +#: dcim/models/device_components.py:166 msgid "Must specify cable end (A or B) when attaching a cable." msgstr "" -#: netbox/dcim/models/device_components.py:170 +#: dcim/models/device_components.py:170 msgid "Cable end must not be set without a cable." msgstr "" -#: netbox/dcim/models/device_components.py:174 +#: dcim/models/device_components.py:174 msgid "Cannot mark as connected with a cable attached." msgstr "" -#: netbox/dcim/models/device_components.py:201 +#: dcim/models/device_components.py:201 #, python-brace-format msgid "{class_name} models must declare a parent_object property" msgstr "" -#: netbox/dcim/models/device_components.py:287 -#: netbox/dcim/models/device_components.py:314 -#: netbox/dcim/models/device_components.py:345 -#: netbox/dcim/models/device_components.py:461 +#: dcim/models/device_components.py:287 dcim/models/device_components.py:314 +#: dcim/models/device_components.py:345 dcim/models/device_components.py:467 msgid "Physical port type" msgstr "" -#: netbox/dcim/models/device_components.py:290 -#: netbox/dcim/models/device_components.py:317 +#: dcim/models/device_components.py:290 dcim/models/device_components.py:317 msgid "speed" msgstr "" -#: netbox/dcim/models/device_components.py:294 -#: netbox/dcim/models/device_components.py:321 +#: dcim/models/device_components.py:294 dcim/models/device_components.py:321 msgid "Port speed in bits per second" msgstr "" -#: netbox/dcim/models/device_components.py:300 +#: dcim/models/device_components.py:300 msgid "console port" msgstr "" -#: netbox/dcim/models/device_components.py:301 +#: dcim/models/device_components.py:301 msgid "console ports" msgstr "" -#: netbox/dcim/models/device_components.py:327 +#: dcim/models/device_components.py:327 msgid "console server port" msgstr "" -#: netbox/dcim/models/device_components.py:328 +#: dcim/models/device_components.py:328 msgid "console server ports" msgstr "" -#: netbox/dcim/models/device_components.py:365 +#: dcim/models/device_components.py:365 msgid "power port" msgstr "" -#: netbox/dcim/models/device_components.py:366 +#: dcim/models/device_components.py:366 msgid "power ports" msgstr "" -#: netbox/dcim/models/device_components.py:486 +#: dcim/models/device_components.py:492 msgid "power outlet" msgstr "" -#: netbox/dcim/models/device_components.py:487 +#: dcim/models/device_components.py:493 msgid "power outlets" msgstr "" -#: netbox/dcim/models/device_components.py:495 +#: dcim/models/device_components.py:501 #, python-brace-format msgid "Parent power port ({power_port}) must belong to the same device" msgstr "" -#: netbox/dcim/models/device_components.py:521 netbox/vpn/models/crypto.py:80 -#: netbox/vpn/models/crypto.py:222 +#: dcim/models/device_components.py:530 vpn/models/crypto.py:80 +#: vpn/models/crypto.py:222 msgid "mode" msgstr "" -#: netbox/dcim/models/device_components.py:526 +#: dcim/models/device_components.py:535 msgid "IEEE 802.1Q tagging strategy" msgstr "" -#: netbox/dcim/models/device_components.py:534 +#: dcim/models/device_components.py:543 msgid "parent interface" msgstr "" -#: netbox/dcim/models/device_components.py:550 +#: dcim/models/device_components.py:559 msgid "untagged VLAN" msgstr "" -#: netbox/dcim/models/device_components.py:556 +#: dcim/models/device_components.py:565 msgid "tagged VLANs" msgstr "" -#: netbox/dcim/models/device_components.py:564 -#: netbox/dcim/tables/devices.py:601 netbox/ipam/forms/bulk_edit.py:510 -#: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 -#: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 -#: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 -#: netbox/templates/virtualization/vminterface.html:60 +#: dcim/models/device_components.py:573 dcim/tables/devices.py:608 +#: ipam/forms/bulk_edit.py:521 ipam/forms/bulk_import.py:514 +#: ipam/forms/filtersets.py:587 ipam/forms/model_forms.py:692 +#: ipam/tables/vlans.py:108 templates/dcim/interface.html:86 +#: templates/ipam/vlan.html:77 templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "" -#: netbox/dcim/models/device_components.py:579 +#: dcim/models/device_components.py:588 msgid "primary MAC address" msgstr "" -#: netbox/dcim/models/device_components.py:591 +#: dcim/models/device_components.py:600 msgid "Only Q-in-Q interfaces may specify a service VLAN." msgstr "" -#: netbox/dcim/models/device_components.py:597 +#: dcim/models/device_components.py:606 #, python-brace-format msgid "MAC address {mac_address} is not assigned to this interface." msgstr "" -#: netbox/dcim/models/device_components.py:653 +#: dcim/models/device_components.py:662 msgid "parent LAG" msgstr "" -#: netbox/dcim/models/device_components.py:663 +#: dcim/models/device_components.py:672 msgid "This interface is used only for out-of-band management" msgstr "" -#: netbox/dcim/models/device_components.py:668 +#: dcim/models/device_components.py:677 msgid "speed (Kbps)" msgstr "" -#: netbox/dcim/models/device_components.py:671 +#: dcim/models/device_components.py:680 msgid "duplex" msgstr "" -#: netbox/dcim/models/device_components.py:681 +#: dcim/models/device_components.py:690 msgid "64-bit World Wide Name" msgstr "" -#: netbox/dcim/models/device_components.py:695 +#: dcim/models/device_components.py:704 msgid "wireless channel" msgstr "" -#: netbox/dcim/models/device_components.py:702 +#: dcim/models/device_components.py:711 msgid "channel frequency (MHz)" msgstr "" -#: netbox/dcim/models/device_components.py:703 -#: netbox/dcim/models/device_components.py:711 +#: dcim/models/device_components.py:712 dcim/models/device_components.py:720 msgid "Populated by selected channel (if set)" msgstr "" -#: netbox/dcim/models/device_components.py:717 +#: dcim/models/device_components.py:726 msgid "transmit power (dBm)" msgstr "" -#: netbox/dcim/models/device_components.py:744 netbox/wireless/models.py:117 +#: dcim/models/device_components.py:753 wireless/models.py:117 msgid "wireless LANs" msgstr "" -#: netbox/dcim/models/device_components.py:792 -#: netbox/virtualization/models/virtualmachines.py:359 +#: dcim/models/device_components.py:801 +#: virtualization/models/virtualmachines.py:364 msgid "interface" msgstr "" -#: netbox/dcim/models/device_components.py:793 -#: netbox/virtualization/models/virtualmachines.py:360 +#: dcim/models/device_components.py:802 +#: virtualization/models/virtualmachines.py:365 msgid "interfaces" msgstr "" -#: netbox/dcim/models/device_components.py:801 +#: dcim/models/device_components.py:810 #, python-brace-format msgid "{display_type} interfaces cannot have a cable attached." msgstr "" -#: netbox/dcim/models/device_components.py:809 +#: dcim/models/device_components.py:818 #, python-brace-format msgid "{display_type} interfaces cannot be marked as connected." msgstr "" -#: netbox/dcim/models/device_components.py:818 -#: netbox/virtualization/models/virtualmachines.py:370 +#: dcim/models/device_components.py:827 +#: virtualization/models/virtualmachines.py:375 msgid "An interface cannot be its own parent." msgstr "" -#: netbox/dcim/models/device_components.py:822 +#: dcim/models/device_components.py:831 msgid "Only virtual interfaces may be assigned to a parent interface." msgstr "" -#: netbox/dcim/models/device_components.py:829 +#: dcim/models/device_components.py:838 #, python-brace-format msgid "" "The selected parent interface ({interface}) belongs to a different device " "({device})" msgstr "" -#: netbox/dcim/models/device_components.py:835 +#: dcim/models/device_components.py:844 #, python-brace-format msgid "" "The selected parent interface ({interface}) belongs to {device}, which is " "not part of virtual chassis {virtual_chassis}." msgstr "" -#: netbox/dcim/models/device_components.py:855 +#: dcim/models/device_components.py:864 #, python-brace-format msgid "" "The selected bridge interface ({bridge}) belongs to a different device " "({device})." msgstr "" -#: netbox/dcim/models/device_components.py:861 +#: dcim/models/device_components.py:870 #, python-brace-format msgid "" "The selected bridge interface ({interface}) belongs to {device}, which is " "not part of virtual chassis {virtual_chassis}." msgstr "" -#: netbox/dcim/models/device_components.py:872 +#: dcim/models/device_components.py:881 msgid "Virtual interfaces cannot have a parent LAG interface." msgstr "" -#: netbox/dcim/models/device_components.py:876 +#: dcim/models/device_components.py:885 msgid "A LAG interface cannot be its own parent." msgstr "" -#: netbox/dcim/models/device_components.py:883 +#: dcim/models/device_components.py:892 #, python-brace-format msgid "" "The selected LAG interface ({lag}) belongs to a different device ({device})." msgstr "" -#: netbox/dcim/models/device_components.py:889 +#: dcim/models/device_components.py:898 #, python-brace-format msgid "" "The selected LAG interface ({lag}) belongs to {device}, which is not part of " "virtual chassis {virtual_chassis}." msgstr "" -#: netbox/dcim/models/device_components.py:900 +#: dcim/models/device_components.py:909 msgid "Virtual interfaces cannot have a PoE mode." msgstr "" -#: netbox/dcim/models/device_components.py:904 +#: dcim/models/device_components.py:913 msgid "Virtual interfaces cannot have a PoE type." msgstr "" -#: netbox/dcim/models/device_components.py:910 +#: dcim/models/device_components.py:919 msgid "Must specify PoE mode when designating a PoE type." msgstr "" -#: netbox/dcim/models/device_components.py:917 +#: dcim/models/device_components.py:926 msgid "Wireless role may be set only on wireless interfaces." msgstr "" -#: netbox/dcim/models/device_components.py:919 +#: dcim/models/device_components.py:928 msgid "Channel may be set only on wireless interfaces." msgstr "" -#: netbox/dcim/models/device_components.py:925 +#: dcim/models/device_components.py:934 msgid "Channel frequency may be set only on wireless interfaces." msgstr "" -#: netbox/dcim/models/device_components.py:929 +#: dcim/models/device_components.py:938 msgid "Cannot specify custom frequency with channel selected." msgstr "" -#: netbox/dcim/models/device_components.py:935 +#: dcim/models/device_components.py:944 msgid "Channel width may be set only on wireless interfaces." msgstr "" -#: netbox/dcim/models/device_components.py:937 +#: dcim/models/device_components.py:946 msgid "Cannot specify custom width with channel selected." msgstr "" -#: netbox/dcim/models/device_components.py:941 +#: dcim/models/device_components.py:950 msgid "Interface mode does not support an untagged vlan." msgstr "" -#: netbox/dcim/models/device_components.py:947 +#: dcim/models/device_components.py:956 #, python-brace-format msgid "" "The untagged VLAN ({untagged_vlan}) must belong to the same site as the " "interface's parent device, or it must be global." msgstr "" -#: netbox/dcim/models/device_components.py:1044 +#: dcim/models/device_components.py:1053 msgid "Mapped position on corresponding rear port" msgstr "" -#: netbox/dcim/models/device_components.py:1060 +#: dcim/models/device_components.py:1069 msgid "front port" msgstr "" -#: netbox/dcim/models/device_components.py:1061 +#: dcim/models/device_components.py:1070 msgid "front ports" msgstr "" -#: netbox/dcim/models/device_components.py:1072 +#: dcim/models/device_components.py:1081 #, python-brace-format msgid "Rear port ({rear_port}) must belong to the same device" msgstr "" -#: netbox/dcim/models/device_components.py:1080 +#: dcim/models/device_components.py:1089 #, python-brace-format msgid "" "Invalid rear port position ({rear_port_position}): Rear port {name} has only " "{positions} positions." msgstr "" -#: netbox/dcim/models/device_components.py:1110 +#: dcim/models/device_components.py:1119 msgid "Number of front ports which may be mapped" msgstr "" -#: netbox/dcim/models/device_components.py:1115 +#: dcim/models/device_components.py:1124 msgid "rear port" msgstr "" -#: netbox/dcim/models/device_components.py:1116 +#: dcim/models/device_components.py:1125 msgid "rear ports" msgstr "" -#: netbox/dcim/models/device_components.py:1127 +#: dcim/models/device_components.py:1136 #, python-brace-format msgid "" "The number of positions cannot be less than the number of mapped front ports " "({frontport_count})" msgstr "" -#: netbox/dcim/models/device_components.py:1168 +#: dcim/models/device_components.py:1177 msgid "module bay" msgstr "" -#: netbox/dcim/models/device_components.py:1169 +#: dcim/models/device_components.py:1178 msgid "module bays" msgstr "" -#: netbox/dcim/models/device_components.py:1183 -#: netbox/dcim/models/devices.py:1229 +#: dcim/models/device_components.py:1192 dcim/models/modules.py:269 msgid "A module bay cannot belong to a module installed within it." msgstr "" -#: netbox/dcim/models/device_components.py:1209 +#: dcim/models/device_components.py:1218 msgid "device bay" msgstr "" -#: netbox/dcim/models/device_components.py:1210 +#: dcim/models/device_components.py:1219 msgid "device bays" msgstr "" -#: netbox/dcim/models/device_components.py:1217 +#: dcim/models/device_components.py:1226 #, python-brace-format msgid "This type of device ({device_type}) does not support device bays." msgstr "" -#: netbox/dcim/models/device_components.py:1223 +#: dcim/models/device_components.py:1232 msgid "Cannot install a device into itself." msgstr "" -#: netbox/dcim/models/device_components.py:1231 +#: dcim/models/device_components.py:1240 #, python-brace-format msgid "" "Cannot install the specified device; device is already installed in {bay}." msgstr "" -#: netbox/dcim/models/device_components.py:1252 +#: dcim/models/device_components.py:1261 msgid "inventory item role" msgstr "" -#: netbox/dcim/models/device_components.py:1253 +#: dcim/models/device_components.py:1262 msgid "inventory item roles" msgstr "" -#: netbox/dcim/models/device_components.py:1313 -#: netbox/dcim/models/devices.py:598 netbox/dcim/models/devices.py:1189 -#: netbox/dcim/models/racks.py:304 -#: netbox/virtualization/models/virtualmachines.py:126 +#: dcim/models/device_components.py:1321 dcim/models/devices.py:486 +#: dcim/models/modules.py:229 dcim/models/racks.py:310 +#: virtualization/models/virtualmachines.py:125 msgid "serial number" msgstr "" -#: netbox/dcim/models/device_components.py:1321 -#: netbox/dcim/models/devices.py:606 netbox/dcim/models/devices.py:1196 -#: netbox/dcim/models/racks.py:311 +#: dcim/models/device_components.py:1329 dcim/models/devices.py:494 +#: dcim/models/modules.py:236 dcim/models/racks.py:317 msgid "asset tag" msgstr "" -#: netbox/dcim/models/device_components.py:1322 +#: dcim/models/device_components.py:1330 msgid "A unique tag used to identify this item" msgstr "" -#: netbox/dcim/models/device_components.py:1325 +#: dcim/models/device_components.py:1333 msgid "discovered" msgstr "" -#: netbox/dcim/models/device_components.py:1327 +#: dcim/models/device_components.py:1335 msgid "This item was automatically discovered" msgstr "" -#: netbox/dcim/models/device_components.py:1345 +#: dcim/models/device_components.py:1353 msgid "inventory item" msgstr "" -#: netbox/dcim/models/device_components.py:1346 +#: dcim/models/device_components.py:1354 msgid "inventory items" msgstr "" -#: netbox/dcim/models/device_components.py:1354 +#: dcim/models/device_components.py:1362 msgid "Cannot assign self as parent." msgstr "" -#: netbox/dcim/models/device_components.py:1362 +#: dcim/models/device_components.py:1370 msgid "Parent inventory item does not belong to the same device." msgstr "" -#: netbox/dcim/models/device_components.py:1368 +#: dcim/models/device_components.py:1376 msgid "Cannot move an inventory item with dependent children" msgstr "" -#: netbox/dcim/models/device_components.py:1376 +#: dcim/models/device_components.py:1384 msgid "Cannot assign inventory item to component on another device" msgstr "" -#: netbox/dcim/models/devices.py:59 +#: dcim/models/devices.py:59 msgid "manufacturer" msgstr "" -#: netbox/dcim/models/devices.py:60 +#: dcim/models/devices.py:60 msgid "manufacturers" msgstr "" -#: netbox/dcim/models/devices.py:84 netbox/dcim/models/devices.py:383 -#: netbox/dcim/models/racks.py:133 +#: dcim/models/devices.py:84 dcim/models/modules.py:85 dcim/models/racks.py:139 msgid "model" msgstr "" -#: netbox/dcim/models/devices.py:97 +#: dcim/models/devices.py:97 msgid "default platform" msgstr "" -#: netbox/dcim/models/devices.py:100 netbox/dcim/models/devices.py:387 +#: dcim/models/devices.py:100 dcim/models/modules.py:89 msgid "part number" msgstr "" -#: netbox/dcim/models/devices.py:103 netbox/dcim/models/devices.py:390 +#: dcim/models/devices.py:103 dcim/models/modules.py:92 msgid "Discrete part number (optional)" msgstr "" -#: netbox/dcim/models/devices.py:109 netbox/dcim/models/racks.py:53 +#: dcim/models/devices.py:109 dcim/models/racks.py:53 msgid "height (U)" msgstr "" -#: netbox/dcim/models/devices.py:113 +#: dcim/models/devices.py:113 msgid "exclude from utilization" msgstr "" -#: netbox/dcim/models/devices.py:114 +#: dcim/models/devices.py:114 msgid "Devices of this type are excluded when calculating rack utilization." msgstr "" -#: netbox/dcim/models/devices.py:118 +#: dcim/models/devices.py:118 msgid "is full depth" msgstr "" -#: netbox/dcim/models/devices.py:119 +#: dcim/models/devices.py:119 msgid "Device consumes both front and rear rack faces." msgstr "" -#: netbox/dcim/models/devices.py:126 +#: dcim/models/devices.py:126 msgid "parent/child status" msgstr "" -#: netbox/dcim/models/devices.py:127 +#: dcim/models/devices.py:127 msgid "" "Parent devices house child devices in device bays. Leave blank if this " "device type is neither a parent nor a child." msgstr "" -#: netbox/dcim/models/devices.py:131 netbox/dcim/models/devices.py:393 -#: netbox/dcim/models/devices.py:651 netbox/dcim/models/racks.py:315 +#: dcim/models/devices.py:131 dcim/models/devices.py:539 +#: dcim/models/modules.py:95 dcim/models/racks.py:321 msgid "airflow" msgstr "" -#: netbox/dcim/models/devices.py:208 +#: dcim/models/devices.py:208 msgid "device type" msgstr "" -#: netbox/dcim/models/devices.py:209 +#: dcim/models/devices.py:209 msgid "device types" msgstr "" -#: netbox/dcim/models/devices.py:291 +#: dcim/models/devices.py:291 msgid "U height must be in increments of 0.5 rack units." msgstr "" -#: netbox/dcim/models/devices.py:308 +#: dcim/models/devices.py:308 #, python-brace-format msgid "" "Device {device} in rack {rack} does not have sufficient space to accommodate " "a height of {height}U" msgstr "" -#: netbox/dcim/models/devices.py:323 +#: dcim/models/devices.py:323 #, python-brace-format msgid "" "Unable to set 0U height: Found {racked_instance_count} " "instances already mounted within racks." msgstr "" -#: netbox/dcim/models/devices.py:332 +#: dcim/models/devices.py:332 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." msgstr "" -#: netbox/dcim/models/devices.py:338 +#: dcim/models/devices.py:338 msgid "Child device types must be 0U." msgstr "" -#: netbox/dcim/models/devices.py:413 -msgid "module type" -msgstr "" - -#: netbox/dcim/models/devices.py:414 -msgid "module types" -msgstr "" - -#: netbox/dcim/models/devices.py:484 +#: dcim/models/devices.py:387 msgid "Virtual machines may be assigned to this role" msgstr "" -#: netbox/dcim/models/devices.py:496 +#: dcim/models/devices.py:401 msgid "device role" msgstr "" -#: netbox/dcim/models/devices.py:497 +#: dcim/models/devices.py:402 msgid "device roles" msgstr "" -#: netbox/dcim/models/devices.py:511 +#: dcim/models/devices.py:416 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" -#: netbox/dcim/models/devices.py:523 +#: dcim/models/devices.py:428 msgid "platform" msgstr "" -#: netbox/dcim/models/devices.py:524 +#: dcim/models/devices.py:429 msgid "platforms" msgstr "" -#: netbox/dcim/models/devices.py:572 +#: dcim/models/devices.py:460 msgid "The function this device serves" msgstr "" -#: netbox/dcim/models/devices.py:599 +#: dcim/models/devices.py:487 msgid "Chassis serial number, assigned by the manufacturer" msgstr "" -#: netbox/dcim/models/devices.py:607 netbox/dcim/models/devices.py:1197 +#: dcim/models/devices.py:495 dcim/models/modules.py:237 msgid "A unique tag used to identify this device" msgstr "" -#: netbox/dcim/models/devices.py:634 +#: dcim/models/devices.py:522 msgid "position (U)" msgstr "" -#: netbox/dcim/models/devices.py:642 +#: dcim/models/devices.py:530 msgid "rack face" msgstr "" -#: netbox/dcim/models/devices.py:663 netbox/dcim/models/devices.py:1425 -#: netbox/virtualization/models/virtualmachines.py:95 +#: dcim/models/devices.py:551 dcim/models/devices.py:1154 +#: virtualization/models/virtualmachines.py:94 msgid "primary IPv4" msgstr "" -#: netbox/dcim/models/devices.py:671 netbox/dcim/models/devices.py:1433 -#: netbox/virtualization/models/virtualmachines.py:103 +#: dcim/models/devices.py:559 dcim/models/devices.py:1162 +#: virtualization/models/virtualmachines.py:102 msgid "primary IPv6" msgstr "" -#: netbox/dcim/models/devices.py:679 +#: dcim/models/devices.py:567 msgid "out-of-band IP" msgstr "" -#: netbox/dcim/models/devices.py:696 +#: dcim/models/devices.py:584 msgid "VC position" msgstr "" -#: netbox/dcim/models/devices.py:699 +#: dcim/models/devices.py:587 msgid "Virtual chassis position" msgstr "" -#: netbox/dcim/models/devices.py:702 +#: dcim/models/devices.py:590 msgid "VC priority" msgstr "" -#: netbox/dcim/models/devices.py:706 +#: dcim/models/devices.py:594 msgid "Virtual chassis master election priority" msgstr "" -#: netbox/dcim/models/devices.py:709 netbox/dcim/models/sites.py:208 +#: dcim/models/devices.py:597 dcim/models/sites.py:208 msgid "latitude" msgstr "" -#: netbox/dcim/models/devices.py:714 netbox/dcim/models/devices.py:722 -#: netbox/dcim/models/sites.py:213 netbox/dcim/models/sites.py:221 +#: dcim/models/devices.py:602 dcim/models/devices.py:610 +#: dcim/models/sites.py:213 dcim/models/sites.py:221 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "" -#: netbox/dcim/models/devices.py:717 netbox/dcim/models/sites.py:216 +#: dcim/models/devices.py:605 dcim/models/sites.py:216 msgid "longitude" msgstr "" -#: netbox/dcim/models/devices.py:790 +#: dcim/models/devices.py:684 msgid "Device name must be unique per site." msgstr "" -#: netbox/dcim/models/devices.py:801 netbox/ipam/models/services.py:71 +#: dcim/models/devices.py:695 msgid "device" msgstr "" -#: netbox/dcim/models/devices.py:802 +#: dcim/models/devices.py:696 msgid "devices" msgstr "" -#: netbox/dcim/models/devices.py:821 +#: dcim/models/devices.py:715 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "" -#: netbox/dcim/models/devices.py:826 +#: dcim/models/devices.py:720 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "" -#: netbox/dcim/models/devices.py:832 +#: dcim/models/devices.py:726 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "" -#: netbox/dcim/models/devices.py:839 +#: dcim/models/devices.py:733 msgid "Cannot select a rack face without assigning a rack." msgstr "" -#: netbox/dcim/models/devices.py:843 +#: dcim/models/devices.py:737 msgid "Cannot select a rack position without assigning a rack." msgstr "" -#: netbox/dcim/models/devices.py:849 +#: dcim/models/devices.py:743 msgid "Position must be in increments of 0.5 rack units." msgstr "" -#: netbox/dcim/models/devices.py:853 +#: dcim/models/devices.py:747 msgid "Must specify rack face when defining rack position." msgstr "" -#: netbox/dcim/models/devices.py:861 +#: dcim/models/devices.py:755 #, python-brace-format msgid "A 0U device type ({device_type}) cannot be assigned to a rack position." msgstr "" -#: netbox/dcim/models/devices.py:872 +#: dcim/models/devices.py:766 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." msgstr "" -#: netbox/dcim/models/devices.py:879 +#: dcim/models/devices.py:773 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." msgstr "" -#: netbox/dcim/models/devices.py:893 +#: dcim/models/devices.py:787 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " "accommodate this device type: {device_type} ({u_height}U)" msgstr "" -#: netbox/dcim/models/devices.py:908 +#: dcim/models/devices.py:802 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "" -#: netbox/dcim/models/devices.py:920 netbox/dcim/models/devices.py:938 +#: dcim/models/devices.py:814 dcim/models/devices.py:832 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "" -#: netbox/dcim/models/devices.py:926 +#: dcim/models/devices.py:820 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "" -#: netbox/dcim/models/devices.py:956 +#: dcim/models/devices.py:850 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " "but this device's type belongs to {devicetype_manufacturer}." msgstr "" -#: netbox/dcim/models/devices.py:967 +#: dcim/models/devices.py:861 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "" -#: netbox/dcim/models/devices.py:974 +#: dcim/models/devices.py:868 #, python-brace-format msgid "The assigned cluster belongs to a different location ({location})" msgstr "" -#: netbox/dcim/models/devices.py:982 +#: dcim/models/devices.py:876 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "" -#: netbox/dcim/models/devices.py:988 +#: dcim/models/devices.py:882 #, python-brace-format msgid "" "Device cannot be removed from virtual chassis {virtual_chassis} because it " "is currently designated as its master." msgstr "" -#: netbox/dcim/models/devices.py:1204 -msgid "module" -msgstr "" - -#: netbox/dcim/models/devices.py:1205 -msgid "modules" -msgstr "" - -#: netbox/dcim/models/devices.py:1218 -#, python-brace-format -msgid "" -"Module must be installed within a module bay belonging to the assigned " -"device ({device})." -msgstr "" - -#: netbox/dcim/models/devices.py:1346 +#: dcim/models/devices.py:1075 msgid "domain" msgstr "" -#: netbox/dcim/models/devices.py:1359 netbox/dcim/models/devices.py:1360 +#: dcim/models/devices.py:1088 dcim/models/devices.py:1089 msgid "virtual chassis" msgstr "" -#: netbox/dcim/models/devices.py:1372 +#: dcim/models/devices.py:1101 #, python-brace-format msgid "The selected master ({master}) is not assigned to this virtual chassis." msgstr "" -#: netbox/dcim/models/devices.py:1388 +#: dcim/models/devices.py:1117 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " "form a cross-chassis LAG interfaces." msgstr "" -#: netbox/dcim/models/devices.py:1414 netbox/vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1143 vpn/models/l2vpn.py:42 msgid "identifier" msgstr "" -#: netbox/dcim/models/devices.py:1415 +#: dcim/models/devices.py:1144 msgid "Numeric identifier unique to the parent device" msgstr "" -#: netbox/dcim/models/devices.py:1443 netbox/extras/models/customfields.py:227 -#: netbox/extras/models/models.py:107 netbox/extras/models/models.py:694 -#: netbox/netbox/models/__init__.py:120 +#: dcim/models/devices.py:1172 extras/models/customfields.py:227 +#: extras/models/models.py:109 extras/models/models.py:767 +#: netbox/models/__init__.py:120 netbox/models/__init__.py:155 msgid "comments" msgstr "" -#: netbox/dcim/models/devices.py:1459 +#: dcim/models/devices.py:1188 msgid "virtual device context" msgstr "" -#: netbox/dcim/models/devices.py:1460 +#: dcim/models/devices.py:1189 msgid "virtual device contexts" msgstr "" -#: netbox/dcim/models/devices.py:1489 +#: dcim/models/devices.py:1218 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "" -#: netbox/dcim/models/devices.py:1495 +#: dcim/models/devices.py:1224 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "" -#: netbox/dcim/models/devices.py:1527 +#: dcim/models/devices.py:1255 msgid "MAC addresses" msgstr "" -#: netbox/dcim/models/devices.py:1559 +#: dcim/models/devices.py:1287 msgid "" "Cannot unassign MAC Address while it is designated as the primary MAC for an " "object" msgstr "" -#: netbox/dcim/models/devices.py:1563 +#: dcim/models/devices.py:1291 msgid "" "Cannot reassign MAC Address while it is designated as the primary MAC for an " "object" msgstr "" -#: netbox/dcim/models/mixins.py:94 +#: dcim/models/mixins.py:92 #, python-brace-format msgid "Please select a {scope_type}." msgstr "" -#: netbox/dcim/models/power.py:55 +#: dcim/models/modules.py:39 +msgid "schema" +msgstr "" + +#: dcim/models/modules.py:46 +msgid "module type profile" +msgstr "" + +#: dcim/models/modules.py:47 +msgid "module type profiles" +msgstr "" + +#: dcim/models/modules.py:104 +msgid "attributes" +msgstr "" + +#: dcim/models/modules.py:120 +msgid "module type" +msgstr "" + +#: dcim/models/modules.py:121 +msgid "module types" +msgstr "" + +#: dcim/models/modules.py:151 +#, python-brace-format +msgid "Invalid schema: {error}" +msgstr "" + +#: dcim/models/modules.py:244 +msgid "module" +msgstr "" + +#: dcim/models/modules.py:245 +msgid "modules" +msgstr "" + +#: dcim/models/modules.py:258 +#, python-brace-format +msgid "" +"Module must be installed within a module bay belonging to the assigned " +"device ({device})." +msgstr "" + +#: dcim/models/power.py:55 msgid "power panel" msgstr "" -#: netbox/dcim/models/power.py:56 +#: dcim/models/power.py:56 msgid "power panels" msgstr "" -#: netbox/dcim/models/power.py:67 +#: dcim/models/power.py:67 #, python-brace-format msgid "" "Location {location} ({location_site}) is in a different site than {site}" msgstr "" -#: netbox/dcim/models/power.py:106 +#: dcim/models/power.py:106 msgid "supply" msgstr "" -#: netbox/dcim/models/power.py:112 +#: dcim/models/power.py:112 msgid "phase" msgstr "" -#: netbox/dcim/models/power.py:118 +#: dcim/models/power.py:118 msgid "voltage" msgstr "" -#: netbox/dcim/models/power.py:123 +#: dcim/models/power.py:123 msgid "amperage" msgstr "" -#: netbox/dcim/models/power.py:128 +#: dcim/models/power.py:128 msgid "max utilization" msgstr "" -#: netbox/dcim/models/power.py:131 +#: dcim/models/power.py:131 msgid "Maximum permissible draw (percentage)" msgstr "" -#: netbox/dcim/models/power.py:134 +#: dcim/models/power.py:134 msgid "available power" msgstr "" -#: netbox/dcim/models/power.py:162 +#: dcim/models/power.py:162 msgid "power feed" msgstr "" -#: netbox/dcim/models/power.py:163 +#: dcim/models/power.py:163 msgid "power feeds" msgstr "" -#: netbox/dcim/models/power.py:174 +#: dcim/models/power.py:174 #, python-brace-format msgid "" "Rack {rack} ({rack_site}) and power panel {powerpanel} ({powerpanel_site}) " "are in different sites." msgstr "" -#: netbox/dcim/models/power.py:185 +#: dcim/models/power.py:185 msgid "Voltage cannot be negative for AC supply" msgstr "" -#: netbox/dcim/models/racks.py:46 +#: dcim/models/racks.py:46 msgid "width" msgstr "" -#: netbox/dcim/models/racks.py:47 +#: dcim/models/racks.py:47 msgid "Rail-to-rail width" msgstr "" -#: netbox/dcim/models/racks.py:55 +#: dcim/models/racks.py:55 msgid "Height in rack units" msgstr "" -#: netbox/dcim/models/racks.py:59 +#: dcim/models/racks.py:59 msgid "starting unit" msgstr "" -#: netbox/dcim/models/racks.py:61 +#: dcim/models/racks.py:61 msgid "Starting unit for rack" msgstr "" -#: netbox/dcim/models/racks.py:65 +#: dcim/models/racks.py:65 msgid "descending units" msgstr "" -#: netbox/dcim/models/racks.py:66 +#: dcim/models/racks.py:66 msgid "Units are numbered top-to-bottom" msgstr "" -#: netbox/dcim/models/racks.py:71 +#: dcim/models/racks.py:71 msgid "outer width" msgstr "" -#: netbox/dcim/models/racks.py:74 +#: dcim/models/racks.py:74 msgid "Outer dimension of rack (width)" msgstr "" -#: netbox/dcim/models/racks.py:77 +#: dcim/models/racks.py:77 +msgid "outer height" +msgstr "" + +#: dcim/models/racks.py:80 +msgid "Outer dimension of rack (height)" +msgstr "" + +#: dcim/models/racks.py:83 msgid "outer depth" msgstr "" -#: netbox/dcim/models/racks.py:80 +#: dcim/models/racks.py:86 msgid "Outer dimension of rack (depth)" msgstr "" -#: netbox/dcim/models/racks.py:83 +#: dcim/models/racks.py:89 msgid "outer unit" msgstr "" -#: netbox/dcim/models/racks.py:90 +#: dcim/models/racks.py:96 msgid "mounting depth" msgstr "" -#: netbox/dcim/models/racks.py:94 +#: dcim/models/racks.py:100 msgid "" "Maximum depth of a mounted device, in millimeters. For four-post racks, this " "is the distance between the front and rear rails." msgstr "" -#: netbox/dcim/models/racks.py:102 +#: dcim/models/racks.py:108 msgid "max weight" msgstr "" -#: netbox/dcim/models/racks.py:105 +#: dcim/models/racks.py:111 msgid "Maximum load capacity for the rack" msgstr "" -#: netbox/dcim/models/racks.py:125 netbox/dcim/models/racks.py:247 +#: dcim/models/racks.py:131 dcim/models/racks.py:253 msgid "form factor" msgstr "" -#: netbox/dcim/models/racks.py:162 +#: dcim/models/racks.py:168 msgid "rack type" msgstr "" -#: netbox/dcim/models/racks.py:163 +#: dcim/models/racks.py:169 msgid "rack types" msgstr "" -#: netbox/dcim/models/racks.py:177 netbox/dcim/models/racks.py:368 -msgid "Must specify a unit when setting an outer width/depth" +#: dcim/models/racks.py:183 dcim/models/racks.py:375 +msgid "Must specify a unit when setting an outer dimension" msgstr "" -#: netbox/dcim/models/racks.py:181 netbox/dcim/models/racks.py:372 +#: dcim/models/racks.py:187 dcim/models/racks.py:379 msgid "Must specify a unit when setting a maximum weight" msgstr "" -#: netbox/dcim/models/racks.py:227 +#: dcim/models/racks.py:233 msgid "rack role" msgstr "" -#: netbox/dcim/models/racks.py:228 +#: dcim/models/racks.py:234 msgid "rack roles" msgstr "" -#: netbox/dcim/models/racks.py:265 +#: dcim/models/racks.py:271 msgid "facility ID" msgstr "" -#: netbox/dcim/models/racks.py:266 +#: dcim/models/racks.py:272 msgid "Locally-assigned identifier" msgstr "" -#: netbox/dcim/models/racks.py:299 netbox/ipam/forms/bulk_import.py:204 -#: netbox/ipam/forms/bulk_import.py:272 netbox/ipam/forms/bulk_import.py:307 -#: netbox/ipam/forms/bulk_import.py:498 -#: netbox/virtualization/forms/bulk_import.py:118 +#: dcim/models/racks.py:305 ipam/forms/bulk_import.py:204 +#: ipam/forms/bulk_import.py:272 ipam/forms/bulk_import.py:307 +#: ipam/forms/bulk_import.py:505 virtualization/forms/bulk_import.py:118 msgid "Functional role" msgstr "" -#: netbox/dcim/models/racks.py:312 +#: dcim/models/racks.py:318 msgid "A unique tag used to identify this rack" msgstr "" -#: netbox/dcim/models/racks.py:351 +#: dcim/models/racks.py:358 msgid "rack" msgstr "" -#: netbox/dcim/models/racks.py:352 +#: dcim/models/racks.py:359 msgid "racks" msgstr "" -#: netbox/dcim/models/racks.py:364 +#: dcim/models/racks.py:371 #, python-brace-format msgid "Assigned location must belong to parent site ({site})." msgstr "" -#: netbox/dcim/models/racks.py:387 +#: dcim/models/racks.py:394 #, python-brace-format msgid "" "Rack must be at least {min_height}U tall to house currently installed " "devices." msgstr "" -#: netbox/dcim/models/racks.py:396 +#: dcim/models/racks.py:403 #, python-brace-format msgid "" "Rack unit numbering must begin at {position} or less to house currently " "installed devices." msgstr "" -#: netbox/dcim/models/racks.py:404 +#: dcim/models/racks.py:411 #, python-brace-format msgid "Location must be from the same site, {site}." msgstr "" -#: netbox/dcim/models/racks.py:666 +#: dcim/models/racks.py:673 msgid "units" msgstr "" -#: netbox/dcim/models/racks.py:692 +#: dcim/models/racks.py:699 msgid "rack reservation" msgstr "" -#: netbox/dcim/models/racks.py:693 +#: dcim/models/racks.py:700 msgid "rack reservations" msgstr "" -#: netbox/dcim/models/racks.py:707 +#: dcim/models/racks.py:714 #, python-brace-format msgid "Invalid unit(s) for {height}U rack: {unit_list}" msgstr "" -#: netbox/dcim/models/racks.py:720 +#: dcim/models/racks.py:727 #, python-brace-format msgid "The following units have already been reserved: {unit_list}" msgstr "" -#: netbox/dcim/models/sites.py:53 +#: dcim/models/sites.py:53 msgid "A top-level region with this name already exists." msgstr "" -#: netbox/dcim/models/sites.py:63 +#: dcim/models/sites.py:63 msgid "A top-level region with this slug already exists." msgstr "" -#: netbox/dcim/models/sites.py:66 +#: dcim/models/sites.py:66 msgid "region" msgstr "" -#: netbox/dcim/models/sites.py:67 +#: dcim/models/sites.py:67 msgid "regions" msgstr "" -#: netbox/dcim/models/sites.py:109 +#: dcim/models/sites.py:109 msgid "A top-level site group with this name already exists." msgstr "" -#: netbox/dcim/models/sites.py:119 +#: dcim/models/sites.py:119 msgid "A top-level site group with this slug already exists." msgstr "" -#: netbox/dcim/models/sites.py:122 +#: dcim/models/sites.py:122 msgid "site group" msgstr "" -#: netbox/dcim/models/sites.py:123 +#: dcim/models/sites.py:123 msgid "site groups" msgstr "" -#: netbox/dcim/models/sites.py:145 +#: dcim/models/sites.py:145 msgid "Full name of the site" msgstr "" -#: netbox/dcim/models/sites.py:181 netbox/dcim/models/sites.py:283 +#: dcim/models/sites.py:181 dcim/models/sites.py:283 msgid "facility" msgstr "" -#: netbox/dcim/models/sites.py:184 netbox/dcim/models/sites.py:286 +#: dcim/models/sites.py:184 dcim/models/sites.py:286 msgid "Local facility ID or description" msgstr "" -#: netbox/dcim/models/sites.py:196 +#: dcim/models/sites.py:196 msgid "physical address" msgstr "" -#: netbox/dcim/models/sites.py:199 +#: dcim/models/sites.py:199 msgid "Physical location of the building" msgstr "" -#: netbox/dcim/models/sites.py:202 +#: dcim/models/sites.py:202 msgid "shipping address" msgstr "" -#: netbox/dcim/models/sites.py:205 +#: dcim/models/sites.py:205 msgid "If different from the physical address" msgstr "" -#: netbox/dcim/models/sites.py:245 +#: dcim/models/sites.py:245 msgid "site" msgstr "" -#: netbox/dcim/models/sites.py:246 +#: dcim/models/sites.py:246 msgid "sites" msgstr "" -#: netbox/dcim/models/sites.py:319 +#: dcim/models/sites.py:319 msgid "A location with this name already exists within the specified site." msgstr "" -#: netbox/dcim/models/sites.py:329 +#: dcim/models/sites.py:329 msgid "A location with this slug already exists within the specified site." msgstr "" -#: netbox/dcim/models/sites.py:332 +#: dcim/models/sites.py:332 msgid "location" msgstr "" -#: netbox/dcim/models/sites.py:333 +#: dcim/models/sites.py:333 msgid "locations" msgstr "" -#: netbox/dcim/models/sites.py:344 +#: dcim/models/sites.py:344 #, python-brace-format msgid "Parent location ({parent}) must belong to the same site ({site})." msgstr "" -#: netbox/dcim/tables/cables.py:55 +#: dcim/tables/cables.py:55 msgid "Termination A" msgstr "" -#: netbox/dcim/tables/cables.py:60 +#: dcim/tables/cables.py:60 msgid "Termination B" msgstr "" -#: netbox/dcim/tables/cables.py:66 netbox/wireless/tables/wirelesslink.py:22 +#: dcim/tables/cables.py:66 wireless/tables/wirelesslink.py:22 msgid "Device A" msgstr "" -#: netbox/dcim/tables/cables.py:72 netbox/wireless/tables/wirelesslink.py:31 +#: dcim/tables/cables.py:72 wireless/tables/wirelesslink.py:31 msgid "Device B" msgstr "" -#: netbox/dcim/tables/cables.py:78 +#: dcim/tables/cables.py:78 msgid "Location A" msgstr "" -#: netbox/dcim/tables/cables.py:84 +#: dcim/tables/cables.py:84 msgid "Location B" msgstr "" -#: netbox/dcim/tables/cables.py:90 +#: dcim/tables/cables.py:90 msgid "Rack A" msgstr "" -#: netbox/dcim/tables/cables.py:96 +#: dcim/tables/cables.py:96 msgid "Rack B" msgstr "" -#: netbox/dcim/tables/cables.py:102 +#: dcim/tables/cables.py:102 msgid "Site A" msgstr "" -#: netbox/dcim/tables/cables.py:108 +#: dcim/tables/cables.py:108 msgid "Site B" msgstr "" -#: netbox/dcim/tables/connections.py:31 netbox/dcim/tables/connections.py:50 -#: netbox/dcim/tables/connections.py:71 -#: netbox/templates/dcim/inc/connection_endpoints.html:16 +#: dcim/tables/connections.py:31 dcim/tables/connections.py:50 +#: dcim/tables/connections.py:71 +#: templates/dcim/inc/connection_endpoints.html:16 msgid "Reachable" msgstr "" -#: netbox/dcim/tables/devices.py:69 netbox/dcim/tables/devices.py:117 -#: netbox/dcim/tables/racks.py:149 netbox/dcim/tables/sites.py:104 -#: netbox/dcim/tables/sites.py:147 netbox/extras/tables/tables.py:551 -#: netbox/netbox/navigation/menu.py:69 netbox/netbox/navigation/menu.py:73 -#: netbox/netbox/navigation/menu.py:75 -#: netbox/virtualization/forms/model_forms.py:122 -#: netbox/virtualization/tables/clusters.py:87 -#: netbox/virtualization/views.py:240 +#: dcim/tables/devices.py:69 dcim/tables/devices.py:117 +#: dcim/tables/racks.py:153 dcim/tables/sites.py:110 dcim/tables/sites.py:153 +#: extras/tables/tables.py:605 netbox/navigation/menu.py:69 +#: netbox/navigation/menu.py:73 netbox/navigation/menu.py:75 +#: virtualization/forms/model_forms.py:122 virtualization/tables/clusters.py:87 +#: virtualization/views.py:234 msgid "Devices" msgstr "" -#: netbox/dcim/tables/devices.py:74 netbox/dcim/tables/devices.py:122 -#: netbox/virtualization/tables/clusters.py:92 +#: dcim/tables/devices.py:74 dcim/tables/devices.py:122 +#: virtualization/tables/clusters.py:92 msgid "VMs" msgstr "" -#: netbox/dcim/tables/devices.py:111 netbox/dcim/tables/devices.py:226 -#: netbox/extras/forms/model_forms.py:644 netbox/templates/dcim/device.html:112 -#: netbox/templates/dcim/devicerole.html:44 -#: netbox/templates/dcim/platform.html:41 -#: netbox/templates/extras/configtemplate.html:10 -#: netbox/templates/extras/object_render_config.html:12 -#: netbox/templates/extras/object_render_config.html:15 -#: netbox/templates/virtualization/virtualmachine.html:48 -#: netbox/virtualization/tables/virtualmachines.py:77 +#: dcim/tables/devices.py:111 dcim/tables/devices.py:226 +#: extras/forms/model_forms.py:712 templates/dcim/device.html:112 +#: templates/dcim/devicerole.html:48 templates/dcim/platform.html:41 +#: templates/extras/configtemplate.html:10 +#: templates/extras/object_render_config.html:12 +#: templates/extras/object_render_config.html:15 +#: templates/virtualization/virtualmachine.html:48 +#: virtualization/tables/virtualmachines.py:77 msgid "Config Template" msgstr "" -#: netbox/dcim/tables/devices.py:197 netbox/dcim/tables/devices.py:1099 -#: netbox/ipam/forms/bulk_import.py:578 netbox/ipam/forms/model_forms.py:316 -#: netbox/ipam/forms/model_forms.py:329 netbox/ipam/tables/ip.py:308 -#: netbox/ipam/tables/ip.py:375 netbox/ipam/tables/ip.py:398 -#: netbox/templates/ipam/ipaddress.html:11 -#: netbox/virtualization/tables/virtualmachines.py:65 +#: dcim/tables/devices.py:197 dcim/tables/devices.py:1106 +#: ipam/forms/bulk_import.py:587 ipam/forms/model_forms.py:316 +#: ipam/forms/model_forms.py:329 ipam/tables/ip.py:314 ipam/tables/ip.py:381 +#: ipam/tables/ip.py:391 ipam/tables/ip.py:414 templates/ipam/ipaddress.html:11 +#: virtualization/tables/virtualmachines.py:65 msgid "IP Address" msgstr "" -#: netbox/dcim/tables/devices.py:201 netbox/dcim/tables/devices.py:1103 -#: netbox/virtualization/tables/virtualmachines.py:56 +#: dcim/tables/devices.py:201 dcim/tables/devices.py:1110 +#: virtualization/tables/virtualmachines.py:56 msgid "IPv4 Address" msgstr "" -#: netbox/dcim/tables/devices.py:205 netbox/dcim/tables/devices.py:1107 -#: netbox/virtualization/tables/virtualmachines.py:60 +#: dcim/tables/devices.py:205 dcim/tables/devices.py:1114 +#: virtualization/tables/virtualmachines.py:60 msgid "IPv6 Address" msgstr "" -#: netbox/dcim/tables/devices.py:220 +#: dcim/tables/devices.py:220 msgid "VC Position" msgstr "" -#: netbox/dcim/tables/devices.py:223 +#: dcim/tables/devices.py:223 msgid "VC Priority" msgstr "" -#: netbox/dcim/tables/devices.py:230 netbox/templates/dcim/device_edit.html:40 -#: netbox/templates/dcim/devicebay_populate.html:16 +#: dcim/tables/devices.py:230 templates/dcim/device_edit.html:40 +#: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "" -#: netbox/dcim/tables/devices.py:235 +#: dcim/tables/devices.py:235 msgid "Position (Device Bay)" msgstr "" -#: netbox/dcim/tables/devices.py:244 +#: dcim/tables/devices.py:244 msgid "Console ports" msgstr "" -#: netbox/dcim/tables/devices.py:247 +#: dcim/tables/devices.py:247 msgid "Console server ports" msgstr "" -#: netbox/dcim/tables/devices.py:250 +#: dcim/tables/devices.py:250 msgid "Power ports" msgstr "" -#: netbox/dcim/tables/devices.py:253 +#: dcim/tables/devices.py:253 msgid "Power outlets" msgstr "" -#: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 -#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 -#: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 -#: netbox/templates/dcim/device/base.html:37 -#: netbox/templates/dcim/device_list.html:43 -#: netbox/templates/dcim/devicetype/base.html:34 -#: netbox/templates/dcim/inc/moduletype_buttons.html:25 -#: netbox/templates/dcim/module.html:34 -#: netbox/templates/dcim/virtualdevicecontext.html:61 -#: netbox/templates/dcim/virtualdevicecontext.html:81 -#: netbox/templates/virtualization/virtualmachine/base.html:27 -#: netbox/templates/virtualization/virtualmachine_list.html:14 -#: netbox/virtualization/tables/virtualmachines.py:71 -#: netbox/virtualization/views.py:405 netbox/wireless/tables/wirelesslan.py:63 +#: dcim/tables/devices.py:256 dcim/tables/devices.py:1119 +#: dcim/tables/devicetypes.py:133 dcim/views.py:1173 dcim/views.py:1473 +#: dcim/views.py:2226 netbox/navigation/menu.py:95 +#: netbox/navigation/menu.py:259 templates/dcim/device/base.html:37 +#: templates/dcim/device_list.html:43 templates/dcim/devicetype/base.html:34 +#: templates/dcim/inc/moduletype_buttons.html:25 templates/dcim/module.html:34 +#: templates/dcim/virtualdevicecontext.html:61 +#: templates/dcim/virtualdevicecontext.html:81 +#: templates/virtualization/virtualmachine/base.html:27 +#: templates/virtualization/virtualmachine_list.html:14 +#: virtualization/tables/virtualmachines.py:71 virtualization/views.py:394 +#: wireless/tables/wirelesslan.py:63 msgid "Interfaces" msgstr "" -#: netbox/dcim/tables/devices.py:259 +#: dcim/tables/devices.py:259 msgid "Front ports" msgstr "" -#: netbox/dcim/tables/devices.py:265 +#: dcim/tables/devices.py:265 msgid "Device bays" msgstr "" -#: netbox/dcim/tables/devices.py:268 +#: dcim/tables/devices.py:268 msgid "Module bays" msgstr "" -#: netbox/dcim/tables/devices.py:271 +#: dcim/tables/devices.py:271 msgid "Inventory items" msgstr "" -#: netbox/dcim/tables/devices.py:314 netbox/dcim/tables/modules.py:57 -#: netbox/templates/dcim/modulebay.html:17 +#: dcim/tables/devices.py:314 dcim/tables/modules.py:91 +#: templates/dcim/module.html:65 templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "" -#: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 -#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 -#: netbox/templates/dcim/device/base.html:52 -#: netbox/templates/dcim/device_list.html:71 -#: netbox/templates/dcim/devicetype/base.html:49 -#: netbox/templates/dcim/inc/panels/inventory_items.html:6 -#: netbox/templates/dcim/inventoryitemrole.html:32 +#: dcim/tables/devices.py:327 dcim/tables/devicetypes.py:52 +#: dcim/tables/devicetypes.py:148 dcim/views.py:1248 dcim/views.py:2324 +#: netbox/navigation/menu.py:104 templates/dcim/device/base.html:52 +#: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 +#: templates/dcim/inc/panels/inventory_items.html:6 +#: templates/dcim/inventoryitemrole.html:32 msgid "Inventory Items" msgstr "" -#: netbox/dcim/tables/devices.py:342 +#: dcim/tables/devices.py:342 msgid "Cable Color" msgstr "" -#: netbox/dcim/tables/devices.py:348 +#: dcim/tables/devices.py:348 msgid "Link Peers" msgstr "" -#: netbox/dcim/tables/devices.py:351 +#: dcim/tables/devices.py:351 msgid "Mark Connected" msgstr "" -#: netbox/dcim/tables/devices.py:470 +#: dcim/tables/devices.py:470 msgid "Maximum draw (W)" msgstr "" -#: netbox/dcim/tables/devices.py:473 +#: dcim/tables/devices.py:473 msgid "Allocated draw (W)" msgstr "" -#: netbox/dcim/tables/devices.py:571 netbox/ipam/forms/model_forms.py:783 -#: netbox/ipam/tables/fhrp.py:28 netbox/ipam/views.py:633 -#: netbox/ipam/views.py:738 netbox/netbox/navigation/menu.py:164 -#: netbox/netbox/navigation/menu.py:166 -#: netbox/templates/dcim/interface.html:396 -#: netbox/templates/ipam/ipaddress_bulk_add.html:15 -#: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:107 -#: netbox/vpn/tables/tunnels.py:98 +#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:785 +#: ipam/tables/fhrp.py:28 ipam/views.py:627 ipam/views.py:727 +#: netbox/navigation/menu.py:165 netbox/navigation/menu.py:167 +#: templates/dcim/interface.html:396 templates/ipam/ipaddress_bulk_add.html:15 +#: templates/ipam/service.html:42 templates/virtualization/vminterface.html:107 +#: vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "" -#: netbox/dcim/tables/devices.py:577 netbox/netbox/navigation/menu.py:210 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:6 +#: dcim/tables/devices.py:584 netbox/navigation/menu.py:211 +#: templates/ipam/inc/panels/fhrp_groups.html:6 msgid "FHRP Groups" msgstr "" -#: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:65 -#: netbox/templates/vpn/tunnel.html:18 -#: netbox/templates/vpn/tunneltermination.html:13 -#: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 -#: netbox/vpn/forms/filtersets.py:46 netbox/vpn/forms/filtersets.py:87 -#: netbox/vpn/forms/model_forms.py:61 netbox/vpn/forms/model_forms.py:146 -#: netbox/vpn/tables/tunnels.py:78 +#: dcim/tables/devices.py:596 templates/dcim/interface.html:95 +#: templates/virtualization/vminterface.html:65 templates/vpn/tunnel.html:18 +#: templates/vpn/tunneltermination.html:13 vpn/forms/bulk_edit.py:76 +#: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:46 +#: vpn/forms/filtersets.py:87 vpn/forms/model_forms.py:61 +#: vpn/forms/model_forms.py:146 vpn/tables/tunnels.py:78 msgid "Tunnel" msgstr "" -#: netbox/dcim/tables/devices.py:625 netbox/dcim/tables/devicetypes.py:234 -#: netbox/templates/dcim/interface.html:65 +#: dcim/tables/devices.py:632 dcim/tables/devicetypes.py:234 +#: templates/dcim/interface.html:65 msgid "Management Only" msgstr "" -#: netbox/dcim/tables/devices.py:644 +#: dcim/tables/devices.py:651 msgid "VDCs" msgstr "" -#: netbox/dcim/tables/devices.py:651 netbox/templates/dcim/interface.html:163 +#: dcim/tables/devices.py:658 templates/dcim/interface.html:163 msgid "Virtual Circuit" msgstr "" -#: netbox/dcim/tables/devices.py:903 netbox/templates/dcim/modulebay.html:53 +#: dcim/tables/devices.py:910 templates/dcim/modulebay.html:53 msgid "Installed Module" msgstr "" -#: netbox/dcim/tables/devices.py:906 +#: dcim/tables/devices.py:913 msgid "Module Serial" msgstr "" -#: netbox/dcim/tables/devices.py:910 +#: dcim/tables/devices.py:917 msgid "Module Asset Tag" msgstr "" -#: netbox/dcim/tables/devices.py:919 +#: dcim/tables/devices.py:926 msgid "Module Status" msgstr "" -#: netbox/dcim/tables/devices.py:973 netbox/dcim/tables/devicetypes.py:319 -#: netbox/templates/dcim/inventoryitem.html:44 +#: dcim/tables/devices.py:980 dcim/tables/devicetypes.py:319 +#: templates/dcim/inventoryitem.html:44 msgid "Component" msgstr "" -#: netbox/dcim/tables/devices.py:1031 +#: dcim/tables/devices.py:1038 msgid "Items" msgstr "" -#: netbox/dcim/tables/devicetypes.py:37 netbox/netbox/navigation/menu.py:60 -#: netbox/netbox/navigation/menu.py:62 +#: dcim/tables/devicetypes.py:37 netbox/navigation/menu.py:60 +#: netbox/navigation/menu.py:62 msgid "Rack Types" msgstr "" -#: netbox/dcim/tables/devicetypes.py:42 netbox/netbox/navigation/menu.py:84 -#: netbox/netbox/navigation/menu.py:86 +#: dcim/tables/devicetypes.py:42 netbox/navigation/menu.py:84 +#: netbox/navigation/menu.py:86 msgid "Device Types" msgstr "" -#: netbox/dcim/tables/devicetypes.py:47 netbox/netbox/navigation/menu.py:87 +#: dcim/tables/devicetypes.py:47 netbox/navigation/menu.py:87 +#: templates/dcim/moduletypeprofile.html:45 msgid "Module Types" msgstr "" -#: netbox/dcim/tables/devicetypes.py:57 netbox/extras/forms/filtersets.py:378 -#: netbox/extras/forms/model_forms.py:551 netbox/extras/tables/tables.py:546 -#: netbox/netbox/navigation/menu.py:78 +#: dcim/tables/devicetypes.py:57 extras/forms/filtersets.py:413 +#: extras/forms/model_forms.py:619 extras/tables/tables.py:600 +#: netbox/navigation/menu.py:78 msgid "Platforms" msgstr "" -#: netbox/dcim/tables/devicetypes.py:89 -#: netbox/templates/dcim/devicetype.html:29 +#: dcim/tables/devicetypes.py:89 templates/dcim/devicetype.html:29 msgid "Default Platform" msgstr "" -#: netbox/dcim/tables/devicetypes.py:93 -#: netbox/templates/dcim/devicetype.html:45 +#: dcim/tables/devicetypes.py:93 templates/dcim/devicetype.html:45 msgid "Full Depth" msgstr "" -#: netbox/dcim/tables/devicetypes.py:103 +#: dcim/tables/devicetypes.py:103 msgid "U Height" msgstr "" -#: netbox/dcim/tables/devicetypes.py:118 netbox/dcim/tables/modules.py:26 -#: netbox/dcim/tables/racks.py:89 +#: dcim/tables/devicetypes.py:118 dcim/tables/modules.py:65 +#: dcim/tables/racks.py:93 msgid "Instances" msgstr "" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 -#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 -#: netbox/netbox/navigation/menu.py:97 -#: netbox/templates/dcim/device/base.html:25 -#: netbox/templates/dcim/device_list.html:15 -#: netbox/templates/dcim/devicetype/base.html:22 -#: netbox/templates/dcim/inc/moduletype_buttons.html:13 -#: netbox/templates/dcim/module.html:22 +#: dcim/tables/devicetypes.py:121 dcim/views.py:1113 dcim/views.py:1413 +#: dcim/views.py:2162 netbox/navigation/menu.py:98 +#: templates/dcim/device/base.html:25 templates/dcim/device_list.html:15 +#: templates/dcim/devicetype/base.html:22 +#: templates/dcim/inc/moduletype_buttons.html:13 templates/dcim/module.html:22 msgid "Console Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 -#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 -#: netbox/netbox/navigation/menu.py:98 -#: netbox/templates/dcim/device/base.html:28 -#: netbox/templates/dcim/device_list.html:22 -#: netbox/templates/dcim/devicetype/base.html:25 -#: netbox/templates/dcim/inc/moduletype_buttons.html:16 -#: netbox/templates/dcim/module.html:25 +#: dcim/tables/devicetypes.py:124 dcim/views.py:1128 dcim/views.py:1428 +#: dcim/views.py:2178 netbox/navigation/menu.py:99 +#: templates/dcim/device/base.html:28 templates/dcim/device_list.html:22 +#: templates/dcim/devicetype/base.html:25 +#: templates/dcim/inc/moduletype_buttons.html:16 templates/dcim/module.html:25 msgid "Console Server Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 -#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 -#: netbox/netbox/navigation/menu.py:99 -#: netbox/templates/dcim/device/base.html:31 -#: netbox/templates/dcim/device_list.html:29 -#: netbox/templates/dcim/devicetype/base.html:28 -#: netbox/templates/dcim/inc/moduletype_buttons.html:19 -#: netbox/templates/dcim/module.html:28 +#: dcim/tables/devicetypes.py:127 dcim/views.py:1143 dcim/views.py:1443 +#: dcim/views.py:2194 netbox/navigation/menu.py:100 +#: templates/dcim/device/base.html:31 templates/dcim/device_list.html:29 +#: templates/dcim/devicetype/base.html:28 +#: templates/dcim/inc/moduletype_buttons.html:19 templates/dcim/module.html:28 msgid "Power Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 -#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 -#: netbox/netbox/navigation/menu.py:100 -#: netbox/templates/dcim/device/base.html:34 -#: netbox/templates/dcim/device_list.html:36 -#: netbox/templates/dcim/devicetype/base.html:31 -#: netbox/templates/dcim/inc/moduletype_buttons.html:22 -#: netbox/templates/dcim/module.html:31 +#: dcim/tables/devicetypes.py:130 dcim/views.py:1158 dcim/views.py:1458 +#: dcim/views.py:2210 netbox/navigation/menu.py:101 +#: templates/dcim/device/base.html:34 templates/dcim/device_list.html:36 +#: templates/dcim/devicetype/base.html:31 +#: templates/dcim/inc/moduletype_buttons.html:22 templates/dcim/module.html:31 msgid "Power Outlets" msgstr "" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 -#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 -#: netbox/netbox/navigation/menu.py:95 -#: netbox/templates/dcim/device/base.html:40 -#: netbox/templates/dcim/devicetype/base.html:37 -#: netbox/templates/dcim/inc/moduletype_buttons.html:28 -#: netbox/templates/dcim/module.html:37 +#: dcim/tables/devicetypes.py:136 dcim/views.py:1188 dcim/views.py:1488 +#: dcim/views.py:2248 netbox/navigation/menu.py:96 +#: templates/dcim/device/base.html:40 templates/dcim/devicetype/base.html:37 +#: templates/dcim/inc/moduletype_buttons.html:28 templates/dcim/module.html:37 msgid "Front Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 -#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 -#: netbox/netbox/navigation/menu.py:96 -#: netbox/templates/dcim/device/base.html:43 -#: netbox/templates/dcim/device_list.html:50 -#: netbox/templates/dcim/devicetype/base.html:40 -#: netbox/templates/dcim/inc/moduletype_buttons.html:31 -#: netbox/templates/dcim/module.html:40 +#: dcim/tables/devicetypes.py:139 dcim/views.py:1203 dcim/views.py:1503 +#: dcim/views.py:2264 netbox/navigation/menu.py:97 +#: templates/dcim/device/base.html:43 templates/dcim/device_list.html:50 +#: templates/dcim/devicetype/base.html:40 +#: templates/dcim/inc/moduletype_buttons.html:31 templates/dcim/module.html:40 msgid "Rear Ports" msgstr "" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 -#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 -#: netbox/templates/dcim/device/base.html:49 -#: netbox/templates/dcim/device_list.html:57 -#: netbox/templates/dcim/devicetype/base.html:46 +#: dcim/tables/devicetypes.py:142 dcim/views.py:1233 dcim/views.py:2304 +#: netbox/navigation/menu.py:103 templates/dcim/device/base.html:49 +#: templates/dcim/device_list.html:57 templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 -#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 -#: netbox/netbox/navigation/menu.py:101 -#: netbox/templates/dcim/device/base.html:46 -#: netbox/templates/dcim/device_list.html:64 -#: netbox/templates/dcim/devicetype/base.html:43 -#: netbox/templates/dcim/inc/moduletype_buttons.html:34 -#: netbox/templates/dcim/module.html:43 +#: dcim/tables/devicetypes.py:145 dcim/views.py:1218 dcim/views.py:1518 +#: dcim/views.py:2284 netbox/navigation/menu.py:102 +#: templates/dcim/device/base.html:46 templates/dcim/device_list.html:64 +#: templates/dcim/devicetype/base.html:43 +#: templates/dcim/inc/moduletype_buttons.html:34 templates/dcim/module.html:43 msgid "Module Bays" msgstr "" -#: netbox/dcim/tables/power.py:36 netbox/netbox/navigation/menu.py:318 -#: netbox/templates/dcim/powerpanel.html:51 +#: dcim/tables/power.py:36 netbox/navigation/menu.py:319 +#: templates/dcim/powerpanel.html:51 msgid "Power Feeds" msgstr "" -#: netbox/dcim/tables/power.py:80 netbox/templates/dcim/powerfeed.html:99 +#: dcim/tables/power.py:80 templates/dcim/powerfeed.html:99 msgid "Max Utilization" msgstr "" -#: netbox/dcim/tables/power.py:84 +#: dcim/tables/power.py:84 msgid "Available Power (VA)" msgstr "" -#: netbox/dcim/tables/racks.py:30 netbox/dcim/tables/sites.py:142 -#: netbox/netbox/navigation/menu.py:43 netbox/netbox/navigation/menu.py:47 -#: netbox/netbox/navigation/menu.py:49 +#: dcim/tables/racks.py:30 dcim/tables/sites.py:148 +#: netbox/navigation/menu.py:43 netbox/navigation/menu.py:47 +#: netbox/navigation/menu.py:49 msgid "Racks" msgstr "" -#: netbox/dcim/tables/racks.py:63 netbox/dcim/tables/racks.py:141 -#: netbox/templates/dcim/device.html:318 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:14 +#: dcim/tables/racks.py:63 dcim/tables/racks.py:145 +#: templates/dcim/device.html:318 +#: templates/dcim/inc/panels/racktype_dimensions.html:14 msgid "Height" msgstr "" -#: netbox/dcim/tables/racks.py:67 netbox/dcim/tables/racks.py:164 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:18 +#: dcim/tables/racks.py:67 dcim/tables/racks.py:168 +#: templates/dcim/inc/panels/racktype_dimensions.html:18 msgid "Outer Width" msgstr "" -#: netbox/dcim/tables/racks.py:71 netbox/dcim/tables/racks.py:168 -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:28 +#: dcim/tables/racks.py:71 dcim/tables/racks.py:172 +#: templates/dcim/inc/panels/racktype_dimensions.html:28 +msgid "Outer Height" +msgstr "" + +#: dcim/tables/racks.py:75 dcim/tables/racks.py:176 +#: templates/dcim/inc/panels/racktype_dimensions.html:38 msgid "Outer Depth" msgstr "" -#: netbox/dcim/tables/racks.py:79 netbox/dcim/tables/racks.py:176 +#: dcim/tables/racks.py:83 dcim/tables/racks.py:184 msgid "Max Weight" msgstr "" -#: netbox/dcim/tables/racks.py:153 +#: dcim/tables/racks.py:157 msgid "Space" msgstr "" -#: netbox/dcim/tables/sites.py:30 netbox/dcim/tables/sites.py:57 -#: netbox/extras/forms/filtersets.py:358 netbox/extras/forms/model_forms.py:531 -#: netbox/ipam/forms/bulk_edit.py:134 netbox/ipam/forms/model_forms.py:159 -#: netbox/ipam/tables/asn.py:66 netbox/netbox/navigation/menu.py:15 -#: netbox/netbox/navigation/menu.py:17 +#: dcim/tables/sites.py:30 dcim/tables/sites.py:60 +#: extras/forms/filtersets.py:393 extras/forms/model_forms.py:599 +#: ipam/forms/bulk_edit.py:134 ipam/forms/model_forms.py:159 +#: ipam/tables/asn.py:66 netbox/navigation/menu.py:15 +#: netbox/navigation/menu.py:19 msgid "Sites" msgstr "" -#: netbox/dcim/tables/sites.py:152 netbox/netbox/navigation/menu.py:202 +#: dcim/tables/sites.py:158 netbox/navigation/menu.py:203 msgid "VLAN Groups" msgstr "" -#: netbox/dcim/tests/test_api.py:50 +#: dcim/tests/test_api.py:50 msgid "Test case must set peer_termination_type" msgstr "" -#: netbox/dcim/views.py:138 +#: dcim/views.py:137 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "" -#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 +#: dcim/views.py:864 netbox/navigation/menu.py:51 msgid "Reservations" msgstr "" -#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 -#: netbox/templates/dcim/site.html:140 +#: dcim/views.py:883 templates/dcim/location.html:91 +#: templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "" -#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 -#: netbox/templates/extras/configcontext.html:10 -#: netbox/virtualization/forms/model_forms.py:232 -#: netbox/virtualization/views.py:446 +#: dcim/views.py:2337 extras/forms/model_forms.py:659 +#: templates/extras/configcontext.html:10 +#: virtualization/forms/model_forms.py:232 virtualization/views.py:435 msgid "Config Context" msgstr "" -#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 +#: dcim/views.py:2347 virtualization/views.py:445 msgid "Render Config" msgstr "" -#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 -#: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 -#: netbox/virtualization/views.py:214 +#: dcim/views.py:2360 extras/tables/tables.py:610 netbox/navigation/menu.py:256 +#: netbox/navigation/menu.py:258 virtualization/views.py:208 msgid "Virtual Machines" msgstr "" -#: netbox/dcim/views.py:3168 +#: dcim/views.py:3188 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "" -#: netbox/dcim/views.py:3209 +#: dcim/views.py:3229 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "" -#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 +#: dcim/views.py:3345 ipam/tables/ip.py:181 msgid "Children" msgstr "" -#: netbox/dcim/views.py:3792 +#: dcim/views.py:3812 #, python-brace-format msgid "Added member {device}" msgstr "" -#: netbox/dcim/views.py:3841 +#: dcim/views.py:3861 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" -#: netbox/dcim/views.py:3854 +#: dcim/views.py:3874 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "" -#: netbox/extras/api/customfields.py:89 +#: extras/api/customfields.py:89 #, python-brace-format msgid "Unknown related object(s): {name}" msgstr "" -#: netbox/extras/api/serializers_/customfields.py:73 +#: extras/api/serializers_/customfields.py:73 msgid "Changing the type of custom fields is not supported." msgstr "" -#: netbox/extras/api/serializers_/scripts.py:70 -#: netbox/extras/api/serializers_/scripts.py:75 +#: extras/api/serializers_/scripts.py:70 extras/api/serializers_/scripts.py:75 msgid "Scheduling is not enabled for this script." msgstr "" -#: netbox/extras/choices.py:30 netbox/extras/forms/misc.py:14 +#: extras/choices.py:30 extras/forms/misc.py:14 msgid "Text" msgstr "" -#: netbox/extras/choices.py:31 +#: extras/choices.py:31 msgid "Text (long)" msgstr "" -#: netbox/extras/choices.py:32 +#: extras/choices.py:32 msgid "Integer" msgstr "" -#: netbox/extras/choices.py:33 +#: extras/choices.py:33 msgid "Decimal" msgstr "" -#: netbox/extras/choices.py:34 +#: extras/choices.py:34 msgid "Boolean (true/false)" msgstr "" -#: netbox/extras/choices.py:35 +#: extras/choices.py:35 msgid "Date" msgstr "" -#: netbox/extras/choices.py:36 +#: extras/choices.py:36 msgid "Date & time" msgstr "" -#: netbox/extras/choices.py:38 +#: extras/choices.py:38 msgid "JSON" msgstr "" -#: netbox/extras/choices.py:39 +#: extras/choices.py:39 msgid "Selection" msgstr "" -#: netbox/extras/choices.py:40 +#: extras/choices.py:40 msgid "Multiple selection" msgstr "" -#: netbox/extras/choices.py:42 +#: extras/choices.py:42 msgid "Multiple objects" msgstr "" -#: netbox/extras/choices.py:53 netbox/netbox/preferences.py:21 -#: netbox/templates/extras/customfield.html:78 netbox/vpn/choices.py:20 -#: netbox/wireless/choices.py:27 -msgid "Disabled" -msgstr "" - -#: netbox/extras/choices.py:54 +#: extras/choices.py:54 msgid "Loose" msgstr "" -#: netbox/extras/choices.py:55 +#: extras/choices.py:55 msgid "Exact" msgstr "" -#: netbox/extras/choices.py:66 +#: extras/choices.py:66 msgid "Always" msgstr "" -#: netbox/extras/choices.py:67 +#: extras/choices.py:67 msgid "If set" msgstr "" -#: netbox/extras/choices.py:68 netbox/extras/choices.py:81 +#: extras/choices.py:68 extras/choices.py:81 msgid "Hidden" msgstr "" -#: netbox/extras/choices.py:79 +#: extras/choices.py:79 msgid "Yes" msgstr "" -#: netbox/extras/choices.py:80 +#: extras/choices.py:80 msgid "No" msgstr "" -#: netbox/extras/choices.py:108 netbox/templates/tenancy/contact.html:57 -#: netbox/tenancy/forms/bulk_edit.py:118 -#: netbox/wireless/forms/model_forms.py:171 +#: extras/choices.py:108 templates/tenancy/contact.html:67 +#: tenancy/forms/bulk_edit.py:125 wireless/forms/model_forms.py:172 msgid "Link" msgstr "" -#: netbox/extras/choices.py:124 +#: extras/choices.py:124 msgid "Newest" msgstr "" -#: netbox/extras/choices.py:125 +#: extras/choices.py:125 msgid "Oldest" msgstr "" -#: netbox/extras/choices.py:126 +#: extras/choices.py:126 msgid "Alphabetical (A-Z)" msgstr "" -#: netbox/extras/choices.py:127 +#: extras/choices.py:127 msgid "Alphabetical (Z-A)" msgstr "" -#: netbox/extras/choices.py:144 netbox/extras/choices.py:165 +#: extras/choices.py:144 extras/choices.py:165 msgid "Info" msgstr "" -#: netbox/extras/choices.py:145 netbox/extras/choices.py:166 +#: extras/choices.py:145 extras/choices.py:166 msgid "Success" msgstr "" -#: netbox/extras/choices.py:146 netbox/extras/choices.py:167 +#: extras/choices.py:146 extras/choices.py:167 msgid "Warning" msgstr "" -#: netbox/extras/choices.py:147 +#: extras/choices.py:147 msgid "Danger" msgstr "" -#: netbox/extras/choices.py:164 +#: extras/choices.py:164 msgid "Debug" msgstr "" -#: netbox/extras/choices.py:168 +#: extras/choices.py:168 msgid "Failure" msgstr "" -#: netbox/extras/choices.py:213 -#: netbox/templates/dcim/virtualchassis_edit.html:111 -#: netbox/templates/generic/bulk_add_component.html:68 -#: netbox/templates/generic/object_edit.html:47 -#: netbox/templates/generic/object_edit.html:80 -#: netbox/templates/htmx/quick_add.html:24 -#: netbox/templates/ipam/inc/ipaddress_edit_header.html:7 -msgid "Create" -msgstr "" - -#: netbox/extras/choices.py:214 -msgid "Update" -msgstr "" - -#: netbox/extras/choices.py:215 -#: netbox/templates/circuits/inc/circuit_termination.html:23 -#: netbox/templates/dcim/inc/panels/inventory_items.html:37 -#: netbox/templates/dcim/powerpanel.html:66 -#: netbox/templates/extras/script_list.html:35 -#: netbox/templates/generic/bulk_delete.html:20 -#: netbox/templates/generic/bulk_delete.html:66 -#: netbox/templates/generic/object_delete.html:19 -#: netbox/templates/htmx/delete_form.html:57 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:48 -#: netbox/templates/users/objectpermission.html:46 -#: netbox/utilities/templates/buttons/delete.html:11 -msgid "Delete" -msgstr "" - -#: netbox/extras/choices.py:239 netbox/netbox/choices.py:59 -#: netbox/netbox/choices.py:104 +#: extras/choices.py:222 netbox/choices.py:59 netbox/choices.py:104 msgid "Blue" msgstr "" -#: netbox/extras/choices.py:240 netbox/netbox/choices.py:58 -#: netbox/netbox/choices.py:105 +#: extras/choices.py:223 netbox/choices.py:58 netbox/choices.py:105 msgid "Indigo" msgstr "" -#: netbox/extras/choices.py:241 netbox/netbox/choices.py:56 -#: netbox/netbox/choices.py:106 +#: extras/choices.py:224 netbox/choices.py:56 netbox/choices.py:106 msgid "Purple" msgstr "" -#: netbox/extras/choices.py:242 netbox/netbox/choices.py:53 -#: netbox/netbox/choices.py:107 +#: extras/choices.py:225 netbox/choices.py:53 netbox/choices.py:107 msgid "Pink" msgstr "" -#: netbox/extras/choices.py:243 netbox/netbox/choices.py:52 -#: netbox/netbox/choices.py:108 +#: extras/choices.py:226 netbox/choices.py:52 netbox/choices.py:108 msgid "Red" msgstr "" -#: netbox/extras/choices.py:244 netbox/netbox/choices.py:70 -#: netbox/netbox/choices.py:109 +#: extras/choices.py:227 netbox/choices.py:70 netbox/choices.py:109 msgid "Orange" msgstr "" -#: netbox/extras/choices.py:245 netbox/netbox/choices.py:68 -#: netbox/netbox/choices.py:110 +#: extras/choices.py:228 netbox/choices.py:68 netbox/choices.py:110 msgid "Yellow" msgstr "" -#: netbox/extras/choices.py:246 netbox/netbox/choices.py:65 -#: netbox/netbox/choices.py:111 +#: extras/choices.py:229 netbox/choices.py:65 netbox/choices.py:111 msgid "Green" msgstr "" -#: netbox/extras/choices.py:247 netbox/netbox/choices.py:62 -#: netbox/netbox/choices.py:112 +#: extras/choices.py:230 netbox/choices.py:62 netbox/choices.py:112 msgid "Teal" msgstr "" -#: netbox/extras/choices.py:248 netbox/netbox/choices.py:61 -#: netbox/netbox/choices.py:113 +#: extras/choices.py:231 netbox/choices.py:61 netbox/choices.py:113 msgid "Cyan" msgstr "" -#: netbox/extras/choices.py:249 netbox/netbox/choices.py:114 +#: extras/choices.py:232 netbox/choices.py:114 msgid "Gray" msgstr "" -#: netbox/extras/choices.py:250 netbox/netbox/choices.py:76 -#: netbox/netbox/choices.py:115 +#: extras/choices.py:233 netbox/choices.py:76 netbox/choices.py:115 msgid "Black" msgstr "" -#: netbox/extras/choices.py:251 netbox/netbox/choices.py:77 -#: netbox/netbox/choices.py:116 +#: extras/choices.py:234 netbox/choices.py:77 netbox/choices.py:116 msgid "White" msgstr "" -#: netbox/extras/choices.py:266 netbox/extras/forms/model_forms.py:367 -#: netbox/extras/forms/model_forms.py:444 -#: netbox/templates/extras/webhook.html:10 +#: extras/choices.py:249 extras/forms/model_forms.py:431 +#: extras/forms/model_forms.py:508 templates/extras/webhook.html:10 msgid "Webhook" msgstr "" -#: netbox/extras/choices.py:267 netbox/extras/forms/model_forms.py:432 -#: netbox/templates/extras/script/base.html:29 +#: extras/choices.py:250 extras/forms/model_forms.py:496 +#: templates/extras/script/base.html:29 msgid "Script" msgstr "" -#: netbox/extras/choices.py:268 +#: extras/choices.py:251 msgid "Notification" msgstr "" -#: netbox/extras/conditions.py:54 +#: extras/conditions.py:54 #, python-brace-format msgid "Unknown operator: {op}. Must be one of: {operators}" msgstr "" -#: netbox/extras/conditions.py:58 +#: extras/conditions.py:58 #, python-brace-format msgid "Unsupported value type: {value}" msgstr "" -#: netbox/extras/conditions.py:60 +#: extras/conditions.py:60 #, python-brace-format msgid "Invalid type for {op} operation: {value}" msgstr "" -#: netbox/extras/conditions.py:137 +#: extras/conditions.py:137 #, python-brace-format msgid "Ruleset must be a dictionary, not {ruleset}." msgstr "" -#: netbox/extras/conditions.py:142 +#: extras/conditions.py:142 msgid "Invalid logic type: must be 'AND' or 'OR'. Please check documentation." msgstr "" -#: netbox/extras/conditions.py:154 +#: extras/conditions.py:154 msgid "Incorrect key(s) informed. Please check documentation." msgstr "" -#: netbox/extras/dashboard/forms.py:38 +#: extras/dashboard/forms.py:38 msgid "Widget type" msgstr "" -#: netbox/extras/dashboard/utils.py:36 +#: extras/dashboard/utils.py:36 #, python-brace-format msgid "Unregistered widget class: {name}" msgstr "" -#: netbox/extras/dashboard/widgets.py:147 +#: extras/dashboard/widgets.py:148 #, python-brace-format msgid "{class_name} must define a render() method." msgstr "" -#: netbox/extras/dashboard/widgets.py:166 +#: extras/dashboard/widgets.py:167 msgid "Note" msgstr "" -#: netbox/extras/dashboard/widgets.py:167 +#: extras/dashboard/widgets.py:168 msgid "Display some arbitrary custom content. Markdown is supported." msgstr "" -#: netbox/extras/dashboard/widgets.py:180 +#: extras/dashboard/widgets.py:181 msgid "Object Counts" msgstr "" -#: netbox/extras/dashboard/widgets.py:181 +#: extras/dashboard/widgets.py:182 msgid "" "Display a set of NetBox models and the number of objects created for each " "type." msgstr "" -#: netbox/extras/dashboard/widgets.py:191 +#: extras/dashboard/widgets.py:192 msgid "Filters to apply when counting the number of objects" msgstr "" -#: netbox/extras/dashboard/widgets.py:199 +#: extras/dashboard/widgets.py:200 msgid "Invalid format. Object filters must be passed as a dictionary." msgstr "" -#: netbox/extras/dashboard/widgets.py:230 +#: extras/dashboard/widgets.py:231 msgid "Object List" msgstr "" -#: netbox/extras/dashboard/widgets.py:231 +#: extras/dashboard/widgets.py:232 msgid "Display an arbitrary list of objects." msgstr "" -#: netbox/extras/dashboard/widgets.py:244 +#: extras/dashboard/widgets.py:245 msgid "The default number of objects to display" msgstr "" -#: netbox/extras/dashboard/widgets.py:256 +#: extras/dashboard/widgets.py:257 msgid "Invalid format. URL parameters must be passed as a dictionary." msgstr "" -#: netbox/extras/dashboard/widgets.py:265 +#: extras/dashboard/widgets.py:266 msgid "Invalid model selection: {self['model'].data} is not supported." msgstr "" -#: netbox/extras/dashboard/widgets.py:307 +#: extras/dashboard/widgets.py:308 msgid "RSS Feed" msgstr "" -#: netbox/extras/dashboard/widgets.py:313 +#: extras/dashboard/widgets.py:314 msgid "Embed an RSS feed from an external website." msgstr "" -#: netbox/extras/dashboard/widgets.py:320 +#: extras/dashboard/widgets.py:321 msgid "Feed URL" msgstr "" -#: netbox/extras/dashboard/widgets.py:324 +#: extras/dashboard/widgets.py:325 msgid "Requires external connection" msgstr "" -#: netbox/extras/dashboard/widgets.py:330 +#: extras/dashboard/widgets.py:331 msgid "The maximum number of objects to display" msgstr "" -#: netbox/extras/dashboard/widgets.py:335 +#: extras/dashboard/widgets.py:336 msgid "How long to stored the cached content (in seconds)" msgstr "" -#: netbox/extras/dashboard/widgets.py:392 netbox/templates/account/base.html:10 -#: netbox/templates/account/bookmarks.html:7 -#: netbox/templates/inc/user_menu.html:43 +#: extras/dashboard/widgets.py:393 templates/account/base.html:10 +#: templates/account/bookmarks.html:7 templates/inc/user_menu.html:43 msgid "Bookmarks" msgstr "" -#: netbox/extras/dashboard/widgets.py:396 +#: extras/dashboard/widgets.py:397 msgid "Show your personal bookmarks" msgstr "" -#: netbox/extras/events.py:151 +#: extras/events.py:151 #, python-brace-format msgid "Unknown action type for an event rule: {action_type}" msgstr "" -#: netbox/extras/events.py:196 +#: extras/events.py:196 #, python-brace-format msgid "Cannot import events pipeline {name} error: {error}" msgstr "" -#: netbox/extras/filtersets.py:45 +#: extras/filtersets.py:49 msgid "Script module (ID)" msgstr "" -#: netbox/extras/filtersets.py:254 netbox/extras/filtersets.py:637 -#: netbox/extras/filtersets.py:665 +#: extras/filtersets.py:258 extras/filtersets.py:730 extras/filtersets.py:758 msgid "Data file (ID)" msgstr "" -#: netbox/extras/filtersets.py:370 netbox/users/filtersets.py:68 -#: netbox/users/filtersets.py:191 +#: extras/filtersets.py:428 users/filtersets.py:68 users/filtersets.py:191 msgid "Group (name)" msgstr "" -#: netbox/extras/filtersets.py:574 -#: netbox/virtualization/forms/filtersets.py:124 +#: extras/filtersets.py:667 virtualization/forms/filtersets.py:124 msgid "Cluster type" msgstr "" -#: netbox/extras/filtersets.py:580 netbox/virtualization/filtersets.py:61 -#: netbox/virtualization/filtersets.py:113 +#: extras/filtersets.py:673 virtualization/filtersets.py:61 +#: virtualization/filtersets.py:113 msgid "Cluster type (slug)" msgstr "" -#: netbox/extras/filtersets.py:601 netbox/tenancy/forms/forms.py:16 -#: netbox/tenancy/forms/forms.py:40 +#: extras/filtersets.py:694 tenancy/forms/forms.py:16 tenancy/forms/forms.py:40 msgid "Tenant group" msgstr "" -#: netbox/extras/filtersets.py:607 netbox/tenancy/filtersets.py:188 -#: netbox/tenancy/filtersets.py:208 +#: extras/filtersets.py:700 tenancy/filtersets.py:193 tenancy/filtersets.py:213 msgid "Tenant group (slug)" msgstr "" -#: netbox/extras/filtersets.py:623 netbox/extras/forms/model_forms.py:509 -#: netbox/templates/extras/tag.html:11 +#: extras/filtersets.py:716 extras/forms/model_forms.py:577 +#: templates/extras/tag.html:11 msgid "Tag" msgstr "" -#: netbox/extras/filtersets.py:629 +#: extras/filtersets.py:722 msgid "Tag (slug)" msgstr "" -#: netbox/extras/filtersets.py:689 netbox/extras/forms/filtersets.py:437 +#: extras/filtersets.py:786 extras/forms/filtersets.py:492 msgid "Has local config context data" msgstr "" -#: netbox/extras/forms/bulk_edit.py:35 netbox/extras/forms/filtersets.py:61 +#: extras/forms/bulk_edit.py:36 extras/forms/filtersets.py:62 msgid "Group name" msgstr "" -#: netbox/extras/forms/bulk_edit.py:43 netbox/extras/forms/filtersets.py:69 -#: netbox/extras/tables/tables.py:68 -#: netbox/templates/extras/customfield.html:38 -#: netbox/templates/generic/bulk_import.html:118 +#: extras/forms/bulk_edit.py:44 extras/forms/filtersets.py:70 +#: extras/tables/tables.py:69 templates/extras/customfield.html:38 +#: templates/generic/bulk_import.html:118 msgid "Required" msgstr "" -#: netbox/extras/forms/bulk_edit.py:48 netbox/extras/forms/filtersets.py:76 +#: extras/forms/bulk_edit.py:49 extras/forms/filtersets.py:77 msgid "Must be unique" msgstr "" -#: netbox/extras/forms/bulk_edit.py:61 netbox/extras/forms/bulk_import.py:60 -#: netbox/extras/forms/filtersets.py:90 -#: netbox/extras/models/customfields.py:211 +#: extras/forms/bulk_edit.py:62 extras/forms/bulk_import.py:60 +#: extras/forms/filtersets.py:91 extras/models/customfields.py:211 msgid "UI visible" msgstr "" -#: netbox/extras/forms/bulk_edit.py:66 netbox/extras/forms/bulk_import.py:66 -#: netbox/extras/forms/filtersets.py:95 -#: netbox/extras/models/customfields.py:218 +#: extras/forms/bulk_edit.py:67 extras/forms/bulk_import.py:66 +#: extras/forms/filtersets.py:96 extras/models/customfields.py:218 msgid "UI editable" msgstr "" -#: netbox/extras/forms/bulk_edit.py:71 netbox/extras/forms/filtersets.py:98 +#: extras/forms/bulk_edit.py:72 extras/forms/filtersets.py:99 msgid "Is cloneable" msgstr "" -#: netbox/extras/forms/bulk_edit.py:76 netbox/extras/forms/filtersets.py:105 +#: extras/forms/bulk_edit.py:77 extras/forms/filtersets.py:106 msgid "Minimum value" msgstr "" -#: netbox/extras/forms/bulk_edit.py:80 netbox/extras/forms/filtersets.py:109 +#: extras/forms/bulk_edit.py:81 extras/forms/filtersets.py:110 msgid "Maximum value" msgstr "" -#: netbox/extras/forms/bulk_edit.py:84 netbox/extras/forms/filtersets.py:113 +#: extras/forms/bulk_edit.py:85 extras/forms/filtersets.py:114 msgid "Validation regex" msgstr "" -#: netbox/extras/forms/bulk_edit.py:91 netbox/extras/forms/filtersets.py:47 -#: netbox/extras/forms/model_forms.py:76 -#: netbox/templates/extras/customfield.html:70 +#: extras/forms/bulk_edit.py:92 extras/forms/filtersets.py:48 +#: extras/forms/model_forms.py:79 templates/extras/customfield.html:70 msgid "Behavior" msgstr "" -#: netbox/extras/forms/bulk_edit.py:128 netbox/extras/forms/filtersets.py:152 +#: extras/forms/bulk_edit.py:129 extras/forms/filtersets.py:153 msgid "New window" msgstr "" -#: netbox/extras/forms/bulk_edit.py:137 +#: extras/forms/bulk_edit.py:138 msgid "Button class" msgstr "" -#: netbox/extras/forms/bulk_edit.py:154 netbox/extras/forms/filtersets.py:191 -#: netbox/extras/models/models.py:409 +#: extras/forms/bulk_edit.py:155 extras/forms/bulk_edit.py:354 +#: extras/forms/filtersets.py:192 extras/forms/filtersets.py:470 +#: extras/models/mixins.py:100 msgid "MIME type" msgstr "" -#: netbox/extras/forms/bulk_edit.py:159 netbox/extras/forms/filtersets.py:194 +#: extras/forms/bulk_edit.py:160 extras/forms/bulk_edit.py:359 +#: extras/forms/filtersets.py:195 extras/forms/filtersets.py:473 +msgid "File name" +msgstr "" + +#: extras/forms/bulk_edit.py:164 extras/forms/bulk_edit.py:363 +#: extras/forms/filtersets.py:199 extras/forms/filtersets.py:477 msgid "File extension" msgstr "" -#: netbox/extras/forms/bulk_edit.py:164 netbox/extras/forms/filtersets.py:198 +#: extras/forms/bulk_edit.py:169 extras/forms/bulk_edit.py:368 +#: extras/forms/filtersets.py:203 extras/forms/filtersets.py:481 msgid "As attachment" msgstr "" -#: netbox/extras/forms/bulk_edit.py:192 netbox/extras/forms/filtersets.py:242 -#: netbox/extras/tables/tables.py:259 -#: netbox/templates/extras/savedfilter.html:29 +#: extras/forms/bulk_edit.py:197 extras/forms/bulk_edit.py:225 +#: extras/forms/filtersets.py:247 extras/forms/filtersets.py:277 +#: extras/tables/tables.py:270 extras/tables/tables.py:303 +#: templates/extras/savedfilter.html:29 templates/extras/tableconfig.html:37 msgid "Shared" msgstr "" -#: netbox/extras/forms/bulk_edit.py:215 netbox/extras/forms/filtersets.py:271 -#: netbox/extras/models/models.py:174 +#: extras/forms/bulk_edit.py:248 extras/forms/filtersets.py:306 +#: extras/models/models.py:176 msgid "HTTP method" msgstr "" -#: netbox/extras/forms/bulk_edit.py:219 netbox/extras/forms/filtersets.py:265 -#: netbox/templates/extras/webhook.html:30 +#: extras/forms/bulk_edit.py:252 extras/forms/filtersets.py:300 +#: templates/extras/webhook.html:30 msgid "Payload URL" msgstr "" -#: netbox/extras/forms/bulk_edit.py:224 netbox/extras/models/models.py:214 +#: extras/forms/bulk_edit.py:257 extras/models/models.py:216 msgid "SSL verification" msgstr "" -#: netbox/extras/forms/bulk_edit.py:227 netbox/templates/extras/webhook.html:38 +#: extras/forms/bulk_edit.py:260 templates/extras/webhook.html:38 msgid "Secret" msgstr "" -#: netbox/extras/forms/bulk_edit.py:232 +#: extras/forms/bulk_edit.py:265 msgid "CA file path" msgstr "" -#: netbox/extras/forms/bulk_edit.py:253 netbox/extras/forms/bulk_import.py:192 -#: netbox/extras/forms/model_forms.py:391 +#: extras/forms/bulk_edit.py:286 extras/forms/bulk_import.py:194 +#: extras/forms/model_forms.py:455 msgid "Event types" msgstr "" -#: netbox/extras/forms/bulk_edit.py:293 +#: extras/forms/bulk_edit.py:330 msgid "Is active" msgstr "" -#: netbox/extras/forms/bulk_import.py:37 netbox/extras/forms/bulk_import.py:118 -#: netbox/extras/forms/bulk_import.py:139 -#: netbox/extras/forms/bulk_import.py:162 -#: netbox/extras/forms/bulk_import.py:186 netbox/extras/forms/filtersets.py:140 -#: netbox/extras/forms/filtersets.py:230 netbox/extras/forms/model_forms.py:47 -#: netbox/extras/forms/model_forms.py:219 -#: netbox/extras/forms/model_forms.py:251 -#: netbox/extras/forms/model_forms.py:292 -#: netbox/extras/forms/model_forms.py:386 -#: netbox/extras/forms/model_forms.py:503 netbox/users/forms/model_forms.py:276 +#: extras/forms/bulk_import.py:37 extras/forms/bulk_import.py:118 +#: extras/forms/bulk_import.py:139 extras/forms/bulk_import.py:164 +#: extras/forms/bulk_import.py:188 extras/forms/filtersets.py:141 +#: extras/forms/filtersets.py:235 extras/forms/filtersets.py:265 +#: extras/forms/model_forms.py:50 extras/forms/model_forms.py:222 +#: extras/forms/model_forms.py:254 extras/forms/model_forms.py:297 +#: extras/forms/model_forms.py:450 extras/forms/model_forms.py:567 +#: users/forms/model_forms.py:276 msgid "Object types" msgstr "" -#: netbox/extras/forms/bulk_import.py:39 netbox/extras/forms/bulk_import.py:120 -#: netbox/extras/forms/bulk_import.py:141 -#: netbox/extras/forms/bulk_import.py:164 -#: netbox/extras/forms/bulk_import.py:188 -#: netbox/tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:39 extras/forms/bulk_import.py:120 +#: extras/forms/bulk_import.py:141 extras/forms/bulk_import.py:166 +#: extras/forms/bulk_import.py:190 tenancy/forms/bulk_import.py:95 msgid "One or more assigned object types" msgstr "" -#: netbox/extras/forms/bulk_import.py:44 +#: extras/forms/bulk_import.py:44 msgid "Field data type (e.g. text, integer, etc.)" msgstr "" -#: netbox/extras/forms/bulk_import.py:47 netbox/extras/forms/filtersets.py:213 -#: netbox/extras/forms/filtersets.py:287 netbox/extras/forms/model_forms.py:318 -#: netbox/extras/forms/model_forms.py:355 netbox/tenancy/forms/filtersets.py:92 +#: extras/forms/bulk_import.py:47 extras/forms/filtersets.py:218 +#: extras/forms/filtersets.py:322 extras/forms/model_forms.py:323 +#: extras/forms/model_forms.py:382 extras/forms/model_forms.py:419 +#: tenancy/forms/filtersets.py:92 msgid "Object type" msgstr "" -#: netbox/extras/forms/bulk_import.py:50 +#: extras/forms/bulk_import.py:50 msgid "Object type (for object or multi-object fields)" msgstr "" -#: netbox/extras/forms/bulk_import.py:53 netbox/extras/forms/filtersets.py:85 +#: extras/forms/bulk_import.py:53 extras/forms/filtersets.py:86 msgid "Choice set" msgstr "" -#: netbox/extras/forms/bulk_import.py:57 +#: extras/forms/bulk_import.py:57 msgid "Choice set (for selection fields)" msgstr "" -#: netbox/extras/forms/bulk_import.py:63 +#: extras/forms/bulk_import.py:63 msgid "Whether the custom field is displayed in the UI" msgstr "" -#: netbox/extras/forms/bulk_import.py:69 +#: extras/forms/bulk_import.py:69 msgid "Whether the custom field is editable in the UI" msgstr "" -#: netbox/extras/forms/bulk_import.py:85 +#: extras/forms/bulk_import.py:85 msgid "The base set of predefined choices to use (if any)" msgstr "" -#: netbox/extras/forms/bulk_import.py:91 +#: extras/forms/bulk_import.py:91 msgid "" "Quoted string of comma-separated field choices with optional labels " "separated by colon: \"choice1:First Choice,choice2:Second Choice\"" msgstr "" -#: netbox/extras/forms/bulk_import.py:123 netbox/extras/models/models.py:323 +#: extras/forms/bulk_import.py:123 extras/models/models.py:325 msgid "button class" msgstr "" -#: netbox/extras/forms/bulk_import.py:126 netbox/extras/models/models.py:327 +#: extras/forms/bulk_import.py:126 extras/models/models.py:329 msgid "" "The class of the first link in a group will be used for the dropdown button" msgstr "" -#: netbox/extras/forms/bulk_import.py:193 +#: extras/forms/bulk_import.py:195 msgid "The event type(s) which will trigger this rule" msgstr "" -#: netbox/extras/forms/bulk_import.py:196 +#: extras/forms/bulk_import.py:198 msgid "Action object" msgstr "" -#: netbox/extras/forms/bulk_import.py:198 +#: extras/forms/bulk_import.py:200 msgid "Webhook name or script as dotted path module.Class" msgstr "" -#: netbox/extras/forms/bulk_import.py:219 +#: extras/forms/bulk_import.py:221 #, python-brace-format msgid "Webhook {name} not found" msgstr "" -#: netbox/extras/forms/bulk_import.py:228 +#: extras/forms/bulk_import.py:230 #, python-brace-format msgid "Script {name} not found" msgstr "" -#: netbox/extras/forms/bulk_import.py:244 +#: extras/forms/bulk_import.py:250 msgid "Assigned object type" msgstr "" -#: netbox/extras/forms/bulk_import.py:249 +#: extras/forms/bulk_import.py:255 msgid "The classification of entry" msgstr "" -#: netbox/extras/forms/bulk_import.py:261 -#: netbox/extras/forms/model_forms.py:334 netbox/netbox/navigation/menu.py:411 -#: netbox/templates/extras/notificationgroup.html:41 -#: netbox/templates/users/group.html:29 netbox/users/forms/model_forms.py:236 -#: netbox/users/forms/model_forms.py:248 netbox/users/forms/model_forms.py:300 -#: netbox/users/tables.py:102 +#: extras/forms/bulk_import.py:267 extras/forms/model_forms.py:398 +#: netbox/navigation/menu.py:413 templates/extras/notificationgroup.html:41 +#: templates/users/group.html:29 users/forms/model_forms.py:236 +#: users/forms/model_forms.py:248 users/forms/model_forms.py:300 +#: users/tables.py:102 msgid "Users" msgstr "" -#: netbox/extras/forms/bulk_import.py:265 +#: extras/forms/bulk_import.py:271 msgid "User names separated by commas, encased with double quotes" msgstr "" -#: netbox/extras/forms/bulk_import.py:268 -#: netbox/extras/forms/model_forms.py:329 netbox/netbox/navigation/menu.py:294 -#: netbox/netbox/navigation/menu.py:431 -#: netbox/templates/extras/notificationgroup.html:31 -#: netbox/users/forms/model_forms.py:181 netbox/users/forms/model_forms.py:193 -#: netbox/users/forms/model_forms.py:305 netbox/users/tables.py:35 -#: netbox/users/tables.py:106 +#: extras/forms/bulk_import.py:274 extras/forms/model_forms.py:393 +#: netbox/navigation/menu.py:295 netbox/navigation/menu.py:433 +#: templates/extras/notificationgroup.html:31 templates/tenancy/contact.html:21 +#: tenancy/forms/bulk_edit.py:139 tenancy/forms/filtersets.py:78 +#: tenancy/forms/model_forms.py:99 tenancy/tables/contacts.py:64 +#: users/forms/model_forms.py:181 users/forms/model_forms.py:193 +#: users/forms/model_forms.py:305 users/tables.py:35 users/tables.py:106 msgid "Groups" msgstr "" -#: netbox/extras/forms/bulk_import.py:272 +#: extras/forms/bulk_import.py:278 msgid "Group names separated by commas, encased with double quotes" msgstr "" -#: netbox/extras/forms/filtersets.py:53 netbox/extras/forms/model_forms.py:56 +#: extras/forms/filtersets.py:54 extras/forms/model_forms.py:59 msgid "Related object type" msgstr "" -#: netbox/extras/forms/filtersets.py:58 +#: extras/forms/filtersets.py:59 msgid "Field type" msgstr "" -#: netbox/extras/forms/filtersets.py:122 netbox/extras/forms/model_forms.py:157 -#: netbox/extras/tables/tables.py:94 -#: netbox/templates/generic/bulk_import.html:154 +#: extras/forms/filtersets.py:123 extras/forms/model_forms.py:160 +#: extras/tables/tables.py:95 templates/generic/bulk_import.html:154 msgid "Choices" msgstr "" -#: netbox/extras/forms/filtersets.py:168 netbox/extras/forms/filtersets.py:326 -#: netbox/extras/forms/filtersets.py:416 netbox/extras/forms/model_forms.py:586 -#: netbox/templates/core/job.html:96 netbox/templates/extras/eventrule.html:84 +#: extras/forms/filtersets.py:169 extras/forms/filtersets.py:361 +#: extras/forms/filtersets.py:451 extras/forms/model_forms.py:654 +#: templates/core/job.html:96 templates/extras/eventrule.html:84 msgid "Data" msgstr "" -#: netbox/extras/forms/filtersets.py:179 netbox/extras/forms/filtersets.py:340 -#: netbox/extras/forms/filtersets.py:426 netbox/netbox/choices.py:132 -#: netbox/utilities/forms/bulk_import.py:26 +#: extras/forms/filtersets.py:170 extras/forms/filtersets.py:452 +#: extras/forms/model_forms.py:267 extras/forms/model_forms.py:715 +msgid "Rendering" +msgstr "" + +#: extras/forms/filtersets.py:180 extras/forms/filtersets.py:375 +#: extras/forms/filtersets.py:462 netbox/choices.py:132 +#: utilities/forms/bulk_import.py:26 msgid "Data file" msgstr "" -#: netbox/extras/forms/filtersets.py:187 +#: extras/forms/filtersets.py:188 msgid "Content types" msgstr "" -#: netbox/extras/forms/filtersets.py:261 netbox/extras/models/models.py:179 +#: extras/forms/filtersets.py:296 extras/models/models.py:181 msgid "HTTP content type" msgstr "" -#: netbox/extras/forms/filtersets.py:292 +#: extras/forms/filtersets.py:327 msgid "Event type" msgstr "" -#: netbox/extras/forms/filtersets.py:297 +#: extras/forms/filtersets.py:332 msgid "Action type" msgstr "" -#: netbox/extras/forms/filtersets.py:313 +#: extras/forms/filtersets.py:348 msgid "Tagged object type" msgstr "" -#: netbox/extras/forms/filtersets.py:318 +#: extras/forms/filtersets.py:353 msgid "Allowed object type" msgstr "" -#: netbox/extras/forms/filtersets.py:348 netbox/extras/forms/model_forms.py:521 -#: netbox/netbox/navigation/menu.py:18 +#: extras/forms/filtersets.py:383 extras/forms/model_forms.py:589 +#: netbox/navigation/menu.py:17 msgid "Regions" msgstr "" -#: netbox/extras/forms/filtersets.py:353 netbox/extras/forms/model_forms.py:526 +#: extras/forms/filtersets.py:388 extras/forms/model_forms.py:594 msgid "Site groups" msgstr "" -#: netbox/extras/forms/filtersets.py:363 netbox/extras/forms/model_forms.py:536 -#: netbox/netbox/navigation/menu.py:20 netbox/templates/dcim/site.html:127 +#: extras/forms/filtersets.py:398 extras/forms/model_forms.py:604 +#: netbox/navigation/menu.py:20 templates/dcim/site.html:127 msgid "Locations" msgstr "" -#: netbox/extras/forms/filtersets.py:368 netbox/extras/forms/model_forms.py:541 +#: extras/forms/filtersets.py:403 extras/forms/model_forms.py:609 msgid "Device types" msgstr "" -#: netbox/extras/forms/filtersets.py:373 netbox/extras/forms/model_forms.py:546 +#: extras/forms/filtersets.py:408 extras/forms/model_forms.py:614 msgid "Roles" msgstr "" -#: netbox/extras/forms/filtersets.py:383 netbox/extras/forms/model_forms.py:556 +#: extras/forms/filtersets.py:418 extras/forms/model_forms.py:624 msgid "Cluster types" msgstr "" -#: netbox/extras/forms/filtersets.py:388 netbox/extras/forms/model_forms.py:561 +#: extras/forms/filtersets.py:423 extras/forms/model_forms.py:629 msgid "Cluster groups" msgstr "" -#: netbox/extras/forms/filtersets.py:393 netbox/extras/forms/model_forms.py:566 -#: netbox/netbox/navigation/menu.py:263 netbox/netbox/navigation/menu.py:265 -#: netbox/templates/virtualization/clustertype.html:30 -#: netbox/virtualization/tables/clusters.py:23 -#: netbox/virtualization/tables/clusters.py:45 +#: extras/forms/filtersets.py:428 extras/forms/model_forms.py:634 +#: netbox/navigation/menu.py:264 netbox/navigation/menu.py:266 +#: templates/virtualization/clustertype.html:30 +#: virtualization/tables/clusters.py:23 virtualization/tables/clusters.py:45 msgid "Clusters" msgstr "" -#: netbox/extras/forms/filtersets.py:398 netbox/extras/forms/model_forms.py:571 +#: extras/forms/filtersets.py:433 extras/forms/model_forms.py:639 msgid "Tenant groups" msgstr "" -#: netbox/extras/forms/model_forms.py:49 +#: extras/forms/model_forms.py:52 msgid "The type(s) of object that have this custom field" msgstr "" -#: netbox/extras/forms/model_forms.py:52 +#: extras/forms/model_forms.py:55 msgid "Default value" msgstr "" -#: netbox/extras/forms/model_forms.py:58 +#: extras/forms/model_forms.py:61 msgid "Type of the related object (for object/multi-object fields only)" msgstr "" -#: netbox/extras/forms/model_forms.py:61 -#: netbox/templates/extras/customfield.html:60 +#: extras/forms/model_forms.py:64 templates/extras/customfield.html:60 msgid "Related object filter" msgstr "" -#: netbox/extras/forms/model_forms.py:63 +#: extras/forms/model_forms.py:66 msgid "Specify query parameters as a JSON object." msgstr "" -#: netbox/extras/forms/model_forms.py:73 -#: netbox/templates/extras/customfield.html:10 +#: extras/forms/model_forms.py:76 templates/extras/customfield.html:10 msgid "Custom Field" msgstr "" -#: netbox/extras/forms/model_forms.py:85 +#: extras/forms/model_forms.py:88 msgid "" "The type of data stored in this field. For object/multi-object fields, " "select the related object type below." msgstr "" -#: netbox/extras/forms/model_forms.py:88 +#: extras/forms/model_forms.py:91 msgid "" "This will be displayed as help text for the form field. Markdown is " "supported." msgstr "" -#: netbox/extras/forms/model_forms.py:143 +#: extras/forms/model_forms.py:146 msgid "Related Object" msgstr "" -#: netbox/extras/forms/model_forms.py:170 +#: extras/forms/model_forms.py:173 msgid "" "Enter one choice per line. An optional label may be specified for each " "choice by appending it with a colon. Example:" msgstr "" -#: netbox/extras/forms/model_forms.py:226 -#: netbox/templates/extras/customlink.html:10 +#: extras/forms/model_forms.py:229 templates/extras/customlink.html:10 msgid "Custom Link" msgstr "" -#: netbox/extras/forms/model_forms.py:228 +#: extras/forms/model_forms.py:231 msgid "Templates" msgstr "" -#: netbox/extras/forms/model_forms.py:240 +#: extras/forms/model_forms.py:243 #, python-brace-format msgid "" "Jinja2 template code for the link text. Reference the object as {example}. " "Links which render as empty text will not be displayed." msgstr "" -#: netbox/extras/forms/model_forms.py:244 +#: extras/forms/model_forms.py:247 #, python-brace-format msgid "" "Jinja2 template code for the link URL. Reference the object as {example}." msgstr "" -#: netbox/extras/forms/model_forms.py:255 -#: netbox/extras/forms/model_forms.py:638 +#: extras/forms/model_forms.py:258 extras/forms/model_forms.py:706 msgid "Template code" msgstr "" -#: netbox/extras/forms/model_forms.py:261 -#: netbox/templates/extras/exporttemplate.html:12 +#: extras/forms/model_forms.py:264 templates/extras/exporttemplate.html:17 msgid "Export Template" msgstr "" -#: netbox/extras/forms/model_forms.py:263 -msgid "Rendering" -msgstr "" - -#: netbox/extras/forms/model_forms.py:277 -#: netbox/extras/forms/model_forms.py:663 +#: extras/forms/model_forms.py:282 extras/forms/model_forms.py:733 msgid "Template content is populated from the remote source selected below." msgstr "" -#: netbox/extras/forms/model_forms.py:284 -#: netbox/extras/forms/model_forms.py:670 +#: extras/forms/model_forms.py:289 extras/forms/model_forms.py:740 msgid "Must specify either local content or a data file" msgstr "" -#: netbox/extras/forms/model_forms.py:298 netbox/netbox/forms/mixins.py:70 -#: netbox/templates/extras/savedfilter.html:10 +#: extras/forms/model_forms.py:303 netbox/forms/mixins.py:70 +#: templates/extras/savedfilter.html:10 msgid "Saved Filter" msgstr "" -#: netbox/extras/forms/model_forms.py:348 +#: extras/forms/model_forms.py:329 templates/account/preferences.html:50 +#: templates/extras/tableconfig.html:62 +msgid "Ordering" +msgstr "" + +#: extras/forms/model_forms.py:331 +msgid "" +"Enter a comma-separated list of column names. Prepend a name with a hyphen " +"to reverse the order." +msgstr "" + +#: extras/forms/model_forms.py:340 utilities/forms/forms.py:118 +msgid "Available Columns" +msgstr "" + +#: extras/forms/model_forms.py:347 utilities/forms/forms.py:126 +msgid "Selected Columns" +msgstr "" + +#: extras/forms/model_forms.py:412 msgid "A notification group specify at least one user or group." msgstr "" -#: netbox/extras/forms/model_forms.py:370 -#: netbox/templates/extras/webhook.html:23 +#: extras/forms/model_forms.py:434 templates/extras/webhook.html:23 msgid "HTTP Request" msgstr "" -#: netbox/extras/forms/model_forms.py:372 -#: netbox/templates/extras/webhook.html:44 +#: extras/forms/model_forms.py:436 templates/extras/webhook.html:44 msgid "SSL" msgstr "" -#: netbox/extras/forms/model_forms.py:394 +#: extras/forms/model_forms.py:458 msgid "Action choice" msgstr "" -#: netbox/extras/forms/model_forms.py:399 +#: extras/forms/model_forms.py:463 msgid "Enter conditions in JSON format." msgstr "" -#: netbox/extras/forms/model_forms.py:403 +#: extras/forms/model_forms.py:467 msgid "" "Enter parameters to pass to the action in JSON format." msgstr "" -#: netbox/extras/forms/model_forms.py:408 -#: netbox/templates/extras/eventrule.html:10 +#: extras/forms/model_forms.py:472 templates/extras/eventrule.html:10 msgid "Event Rule" msgstr "" -#: netbox/extras/forms/model_forms.py:409 +#: extras/forms/model_forms.py:473 msgid "Triggers" msgstr "" -#: netbox/extras/forms/model_forms.py:456 +#: extras/forms/model_forms.py:520 msgid "Notification group" msgstr "" -#: netbox/extras/forms/model_forms.py:576 netbox/netbox/navigation/menu.py:26 -#: netbox/tenancy/tables/tenants.py:22 +#: extras/forms/model_forms.py:644 netbox/navigation/menu.py:26 +#: tenancy/tables/tenants.py:22 msgid "Tenants" msgstr "" -#: netbox/extras/forms/model_forms.py:620 +#: extras/forms/model_forms.py:688 msgid "Data is populated from the remote source selected below." msgstr "" -#: netbox/extras/forms/model_forms.py:626 +#: extras/forms/model_forms.py:694 msgid "Must specify either local data or a data file" msgstr "" -#: netbox/extras/forms/model_forms.py:645 -#: netbox/templates/core/datafile.html:55 -msgid "Content" -msgstr "" - -#: netbox/extras/forms/reports.py:17 netbox/extras/forms/scripts.py:23 +#: extras/forms/reports.py:17 extras/forms/scripts.py:30 msgid "Schedule at" msgstr "" -#: netbox/extras/forms/reports.py:18 +#: extras/forms/reports.py:18 msgid "Schedule execution of report to a set time" msgstr "" -#: netbox/extras/forms/reports.py:23 netbox/extras/forms/scripts.py:29 +#: extras/forms/reports.py:23 extras/forms/scripts.py:36 msgid "Recurs every" msgstr "" -#: netbox/extras/forms/reports.py:27 +#: extras/forms/reports.py:27 msgid "Interval at which this report is re-run (in minutes)" msgstr "" -#: netbox/extras/forms/reports.py:35 netbox/extras/forms/scripts.py:41 +#: extras/forms/reports.py:35 extras/forms/scripts.py:48 #, python-brace-format msgid " (current time: {now})" msgstr "" -#: netbox/extras/forms/reports.py:45 netbox/extras/forms/scripts.py:51 +#: extras/forms/reports.py:45 extras/forms/scripts.py:58 msgid "Scheduled time must be in the future." msgstr "" -#: netbox/extras/forms/scripts.py:17 +#: extras/forms/scripts.py:24 msgid "Commit changes" msgstr "" -#: netbox/extras/forms/scripts.py:18 +#: extras/forms/scripts.py:25 msgid "Commit changes to the database (uncheck for a dry-run)" msgstr "" -#: netbox/extras/forms/scripts.py:24 +#: extras/forms/scripts.py:31 msgid "Schedule execution of script to a set time" msgstr "" -#: netbox/extras/forms/scripts.py:33 +#: extras/forms/scripts.py:40 msgid "Interval at which this script is re-run (in minutes)" msgstr "" -#: netbox/extras/jobs.py:47 +#: extras/jobs.py:47 msgid "Database changes have been reverted automatically." msgstr "" -#: netbox/extras/jobs.py:53 +#: extras/jobs.py:53 msgid "Script aborted with error: " msgstr "" -#: netbox/extras/jobs.py:63 +#: extras/jobs.py:63 msgid "An exception occurred: " msgstr "" -#: netbox/extras/jobs.py:68 +#: extras/jobs.py:68 msgid "Database changes have been reverted due to error." msgstr "" -#: netbox/extras/management/commands/reindex.py:67 +#: extras/management/commands/reindex.py:67 msgid "No indexers found!" msgstr "" -#: netbox/extras/models/configs.py:41 netbox/extras/models/models.py:313 -#: netbox/extras/models/models.py:522 netbox/extras/models/search.py:48 -#: netbox/ipam/models/ip.py:188 netbox/netbox/models/mixins.py:15 +#: extras/models/configs.py:38 extras/models/models.py:315 +#: extras/models/models.py:480 extras/models/models.py:559 +#: extras/models/search.py:48 extras/models/tags.py:44 ipam/models/ip.py:188 +#: netbox/models/mixins.py:15 msgid "weight" msgstr "" -#: netbox/extras/models/configs.py:130 +#: extras/models/configs.py:127 msgid "config context" msgstr "" -#: netbox/extras/models/configs.py:131 +#: extras/models/configs.py:128 msgid "config contexts" msgstr "" -#: netbox/extras/models/configs.py:149 netbox/extras/models/configs.py:205 +#: extras/models/configs.py:146 extras/models/configs.py:202 msgid "JSON data must be in object form. Example:" msgstr "" -#: netbox/extras/models/configs.py:169 +#: extras/models/configs.py:166 msgid "" "Local config context data takes precedence over source contexts in the final " "rendered config context" msgstr "" -#: netbox/extras/models/configs.py:224 -msgid "template code" -msgstr "" - -#: netbox/extras/models/configs.py:225 -msgid "Jinja2 template code." -msgstr "" - -#: netbox/extras/models/configs.py:228 -msgid "environment parameters" -msgstr "" - -#: netbox/extras/models/configs.py:233 -msgid "" -"Any additional parameters to pass when constructing the Jinja2 " -"environment." -msgstr "" - -#: netbox/extras/models/configs.py:240 +#: extras/models/configs.py:225 msgid "config template" msgstr "" -#: netbox/extras/models/configs.py:241 +#: extras/models/configs.py:226 msgid "config templates" msgstr "" -#: netbox/extras/models/customfields.py:77 +#: extras/models/customfields.py:77 msgid "The object(s) to which this field applies." msgstr "" -#: netbox/extras/models/customfields.py:84 +#: extras/models/customfields.py:84 msgid "The type of data this custom field holds" msgstr "" -#: netbox/extras/models/customfields.py:91 +#: extras/models/customfields.py:91 msgid "The type of NetBox object this field maps to (for object fields)" msgstr "" -#: netbox/extras/models/customfields.py:97 +#: extras/models/customfields.py:97 msgid "Internal field name" msgstr "" -#: netbox/extras/models/customfields.py:101 +#: extras/models/customfields.py:101 msgid "Only alphanumeric characters and underscores are allowed." msgstr "" -#: netbox/extras/models/customfields.py:106 +#: extras/models/customfields.py:106 msgid "Double underscores are not permitted in custom field names." msgstr "" -#: netbox/extras/models/customfields.py:117 +#: extras/models/customfields.py:117 msgid "" "Name of the field as displayed to users (if not provided, 'the field's name " "will be used)" msgstr "" -#: netbox/extras/models/customfields.py:121 netbox/extras/models/models.py:317 +#: extras/models/customfields.py:121 extras/models/models.py:319 msgid "group name" msgstr "" -#: netbox/extras/models/customfields.py:124 +#: extras/models/customfields.py:124 msgid "Custom fields within the same group will be displayed together" msgstr "" -#: netbox/extras/models/customfields.py:132 +#: extras/models/customfields.py:132 msgid "required" msgstr "" -#: netbox/extras/models/customfields.py:134 +#: extras/models/customfields.py:134 msgid "" "This field is required when creating new objects or editing an existing " "object." msgstr "" -#: netbox/extras/models/customfields.py:137 +#: extras/models/customfields.py:137 msgid "must be unique" msgstr "" -#: netbox/extras/models/customfields.py:139 +#: extras/models/customfields.py:139 msgid "The value of this field must be unique for the assigned object" msgstr "" -#: netbox/extras/models/customfields.py:142 +#: extras/models/customfields.py:142 msgid "search weight" msgstr "" -#: netbox/extras/models/customfields.py:145 +#: extras/models/customfields.py:145 msgid "" "Weighting for search. Lower values are considered more important. Fields " "with a search weight of zero will be ignored." msgstr "" -#: netbox/extras/models/customfields.py:150 +#: extras/models/customfields.py:150 msgid "filter logic" msgstr "" -#: netbox/extras/models/customfields.py:154 +#: extras/models/customfields.py:154 msgid "" "Loose matches any instance of a given string; exact matches the entire field." msgstr "" -#: netbox/extras/models/customfields.py:157 +#: extras/models/customfields.py:157 msgid "default" msgstr "" -#: netbox/extras/models/customfields.py:161 +#: extras/models/customfields.py:161 msgid "" "Default value for the field (must be a JSON value). Encapsulate strings with " "double quotes (e.g. \"Foo\")." msgstr "" -#: netbox/extras/models/customfields.py:168 +#: extras/models/customfields.py:168 msgid "" "Filter the object selection choices using a query_params dict (must be a " "JSON value).Encapsulate strings with double quotes (e.g. \"Foo\")." msgstr "" -#: netbox/extras/models/customfields.py:174 +#: extras/models/customfields.py:174 msgid "display weight" msgstr "" -#: netbox/extras/models/customfields.py:175 +#: extras/models/customfields.py:175 msgid "Fields with higher weights appear lower in a form." msgstr "" -#: netbox/extras/models/customfields.py:180 +#: extras/models/customfields.py:180 msgid "minimum value" msgstr "" -#: netbox/extras/models/customfields.py:181 +#: extras/models/customfields.py:181 msgid "Minimum allowed value (for numeric fields)" msgstr "" -#: netbox/extras/models/customfields.py:186 +#: extras/models/customfields.py:186 msgid "maximum value" msgstr "" -#: netbox/extras/models/customfields.py:187 +#: extras/models/customfields.py:187 msgid "Maximum allowed value (for numeric fields)" msgstr "" -#: netbox/extras/models/customfields.py:193 +#: extras/models/customfields.py:193 msgid "validation regex" msgstr "" -#: netbox/extras/models/customfields.py:195 +#: extras/models/customfields.py:195 #, python-brace-format msgid "" "Regular expression to enforce on text field values. Use ^ and $ to force " @@ -8685,261 +8199,308 @@ msgid "" "values to exactly three uppercase letters." msgstr "" -#: netbox/extras/models/customfields.py:203 +#: extras/models/customfields.py:203 msgid "choice set" msgstr "" -#: netbox/extras/models/customfields.py:212 +#: extras/models/customfields.py:212 msgid "Specifies whether the custom field is displayed in the UI" msgstr "" -#: netbox/extras/models/customfields.py:219 +#: extras/models/customfields.py:219 msgid "Specifies whether the custom field value can be edited in the UI" msgstr "" -#: netbox/extras/models/customfields.py:223 +#: extras/models/customfields.py:223 msgid "is cloneable" msgstr "" -#: netbox/extras/models/customfields.py:224 +#: extras/models/customfields.py:224 msgid "Replicate this value when cloning objects" msgstr "" -#: netbox/extras/models/customfields.py:241 +#: extras/models/customfields.py:241 msgid "custom field" msgstr "" -#: netbox/extras/models/customfields.py:242 +#: extras/models/customfields.py:242 msgid "custom fields" msgstr "" -#: netbox/extras/models/customfields.py:344 +#: extras/models/customfields.py:344 #, python-brace-format msgid "Invalid default value \"{value}\": {error}" msgstr "" -#: netbox/extras/models/customfields.py:351 +#: extras/models/customfields.py:351 msgid "A minimum value may be set only for numeric fields" msgstr "" -#: netbox/extras/models/customfields.py:353 +#: extras/models/customfields.py:353 msgid "A maximum value may be set only for numeric fields" msgstr "" -#: netbox/extras/models/customfields.py:363 +#: extras/models/customfields.py:363 msgid "Regular expression validation is supported only for text and URL fields" msgstr "" -#: netbox/extras/models/customfields.py:369 +#: extras/models/customfields.py:369 msgid "Uniqueness cannot be enforced for boolean fields" msgstr "" -#: netbox/extras/models/customfields.py:379 +#: extras/models/customfields.py:379 msgid "Selection fields must specify a set of choices." msgstr "" -#: netbox/extras/models/customfields.py:383 +#: extras/models/customfields.py:383 msgid "Choices may be set only on selection fields." msgstr "" -#: netbox/extras/models/customfields.py:390 +#: extras/models/customfields.py:390 msgid "Object fields must define an object type." msgstr "" -#: netbox/extras/models/customfields.py:394 +#: extras/models/customfields.py:394 #, python-brace-format msgid "{type} fields may not define an object type." msgstr "" -#: netbox/extras/models/customfields.py:401 +#: extras/models/customfields.py:401 msgid "A related object filter can be defined only for object fields." msgstr "" -#: netbox/extras/models/customfields.py:405 +#: extras/models/customfields.py:405 msgid "Filter must be defined as a dictionary mapping attributes to values." msgstr "" -#: netbox/extras/models/customfields.py:484 +#: extras/models/customfields.py:484 msgid "True" msgstr "" -#: netbox/extras/models/customfields.py:485 +#: extras/models/customfields.py:485 msgid "False" msgstr "" -#: netbox/extras/models/customfields.py:577 +#: extras/models/customfields.py:577 #, python-brace-format msgid "Values must match this regex: {regex}" msgstr "" -#: netbox/extras/models/customfields.py:671 +#: extras/models/customfields.py:671 msgid "Value must be a string." msgstr "" -#: netbox/extras/models/customfields.py:673 +#: extras/models/customfields.py:673 #, python-brace-format msgid "Value must match regex '{regex}'" msgstr "" -#: netbox/extras/models/customfields.py:678 +#: extras/models/customfields.py:678 msgid "Value must be an integer." msgstr "" -#: netbox/extras/models/customfields.py:681 -#: netbox/extras/models/customfields.py:696 +#: extras/models/customfields.py:681 extras/models/customfields.py:696 #, python-brace-format msgid "Value must be at least {minimum}" msgstr "" -#: netbox/extras/models/customfields.py:685 -#: netbox/extras/models/customfields.py:700 +#: extras/models/customfields.py:685 extras/models/customfields.py:700 #, python-brace-format msgid "Value must not exceed {maximum}" msgstr "" -#: netbox/extras/models/customfields.py:693 +#: extras/models/customfields.py:693 msgid "Value must be a decimal." msgstr "" -#: netbox/extras/models/customfields.py:705 +#: extras/models/customfields.py:705 msgid "Value must be true or false." msgstr "" -#: netbox/extras/models/customfields.py:713 +#: extras/models/customfields.py:713 msgid "Date values must be in ISO 8601 format (YYYY-MM-DD)." msgstr "" -#: netbox/extras/models/customfields.py:722 +#: extras/models/customfields.py:722 msgid "Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS)." msgstr "" -#: netbox/extras/models/customfields.py:729 +#: extras/models/customfields.py:729 #, python-brace-format msgid "Invalid choice ({value}) for choice set {choiceset}." msgstr "" -#: netbox/extras/models/customfields.py:739 +#: extras/models/customfields.py:739 #, python-brace-format msgid "Invalid choice(s) ({value}) for choice set {choiceset}." msgstr "" -#: netbox/extras/models/customfields.py:748 +#: extras/models/customfields.py:748 #, python-brace-format msgid "Value must be an object ID, not {type}" msgstr "" -#: netbox/extras/models/customfields.py:754 +#: extras/models/customfields.py:754 #, python-brace-format msgid "Value must be a list of object IDs, not {type}" msgstr "" -#: netbox/extras/models/customfields.py:758 +#: extras/models/customfields.py:758 #, python-brace-format msgid "Found invalid object ID: {id}" msgstr "" -#: netbox/extras/models/customfields.py:761 +#: extras/models/customfields.py:761 msgid "Required field cannot be empty." msgstr "" -#: netbox/extras/models/customfields.py:781 +#: extras/models/customfields.py:781 msgid "Base set of predefined choices (optional)" msgstr "" -#: netbox/extras/models/customfields.py:793 +#: extras/models/customfields.py:793 msgid "Choices are automatically ordered alphabetically" msgstr "" -#: netbox/extras/models/customfields.py:800 +#: extras/models/customfields.py:800 msgid "custom field choice set" msgstr "" -#: netbox/extras/models/customfields.py:801 +#: extras/models/customfields.py:801 msgid "custom field choice sets" msgstr "" -#: netbox/extras/models/customfields.py:843 +#: extras/models/customfields.py:843 msgid "Must define base or extra choices." msgstr "" -#: netbox/extras/models/customfields.py:867 +#: extras/models/customfields.py:867 #, python-brace-format msgid "" "Cannot remove choice {choice} as there are {model} objects which reference " "it." msgstr "" -#: netbox/extras/models/dashboard.py:18 +#: extras/models/dashboard.py:18 msgid "layout" msgstr "" -#: netbox/extras/models/dashboard.py:22 +#: extras/models/dashboard.py:22 msgid "config" msgstr "" -#: netbox/extras/models/dashboard.py:27 +#: extras/models/dashboard.py:27 msgid "dashboard" msgstr "" -#: netbox/extras/models/dashboard.py:28 +#: extras/models/dashboard.py:28 msgid "dashboards" msgstr "" -#: netbox/extras/models/models.py:52 +#: extras/models/mixins.py:85 +msgid "template code" +msgstr "" + +#: extras/models/mixins.py:86 +msgid "Jinja template code." +msgstr "" + +#: extras/models/mixins.py:89 +msgid "environment parameters" +msgstr "" + +#: extras/models/mixins.py:94 +#, python-brace-format +msgid "" +"Any additional parameters to pass when constructing " +"the Jinja environment" +msgstr "" + +#: extras/models/mixins.py:101 +#, python-brace-format +msgid "Defaults to {default}" +msgstr "" + +#: extras/models/mixins.py:106 +msgid "Filename to give to the rendered export file" +msgstr "" + +#: extras/models/mixins.py:109 +msgid "file extension" +msgstr "" + +#: extras/models/mixins.py:112 +msgid "Extension to append to the rendered filename" +msgstr "" + +#: extras/models/mixins.py:115 +msgid "as attachment" +msgstr "" + +#: extras/models/mixins.py:117 +msgid "Download file as attachment" +msgstr "" + +#: extras/models/mixins.py:124 +#, python-brace-format +msgid "{class_name} must implement a get_context() method." +msgstr "" + +#: extras/models/models.py:54 msgid "object types" msgstr "" -#: netbox/extras/models/models.py:53 +#: extras/models/models.py:55 msgid "The object(s) to which this rule applies." msgstr "" -#: netbox/extras/models/models.py:67 +#: extras/models/models.py:69 msgid "The types of event which will trigger this rule." msgstr "" -#: netbox/extras/models/models.py:74 +#: extras/models/models.py:76 msgid "conditions" msgstr "" -#: netbox/extras/models/models.py:77 +#: extras/models/models.py:79 msgid "" "A set of conditions which determine whether the event will be generated." msgstr "" -#: netbox/extras/models/models.py:85 +#: extras/models/models.py:87 msgid "action type" msgstr "" -#: netbox/extras/models/models.py:104 +#: extras/models/models.py:106 msgid "Additional data to pass to the action object" msgstr "" -#: netbox/extras/models/models.py:116 +#: extras/models/models.py:118 msgid "event rule" msgstr "" -#: netbox/extras/models/models.py:117 +#: extras/models/models.py:119 msgid "event rules" msgstr "" -#: netbox/extras/models/models.py:166 +#: extras/models/models.py:168 msgid "" "This URL will be called using the HTTP method defined when the webhook is " "called. Jinja2 template processing is supported with the same context as the " "request body." msgstr "" -#: netbox/extras/models/models.py:181 +#: extras/models/models.py:183 msgid "" "The complete list of official content types is available here." msgstr "" -#: netbox/extras/models/models.py:186 +#: extras/models/models.py:188 msgid "additional headers" msgstr "" -#: netbox/extras/models/models.py:189 +#: extras/models/models.py:191 msgid "" "User-supplied HTTP headers to be sent with the request in addition to the " "HTTP content type. Headers should be defined in the format Name: " @@ -8947,11 +8508,11 @@ msgid "" "as the request body (below)." msgstr "" -#: netbox/extras/models/models.py:195 +#: extras/models/models.py:197 msgid "body template" msgstr "" -#: netbox/extras/models/models.py:198 +#: extras/models/models.py:200 msgid "" "Jinja2 template for a custom request body. If blank, a JSON object " "representing the change will be included. Available context data includes: " @@ -8959,4537 +8520,4499 @@ msgid "" "username, request_id, and data." msgstr "" -#: netbox/extras/models/models.py:204 +#: extras/models/models.py:206 msgid "secret" msgstr "" -#: netbox/extras/models/models.py:208 +#: extras/models/models.py:210 msgid "" "When provided, the request will include a X-Hook-Signature " "header containing a HMAC hex digest of the payload body using the secret as " "the key. The secret is not transmitted in the request." msgstr "" -#: netbox/extras/models/models.py:215 +#: extras/models/models.py:217 msgid "Enable SSL certificate verification. Disable with caution!" msgstr "" -#: netbox/extras/models/models.py:221 netbox/templates/extras/webhook.html:51 +#: extras/models/models.py:223 templates/extras/webhook.html:51 msgid "CA File Path" msgstr "" -#: netbox/extras/models/models.py:223 +#: extras/models/models.py:225 msgid "" "The specific CA certificate file to use for SSL verification. Leave blank to " "use the system defaults." msgstr "" -#: netbox/extras/models/models.py:234 +#: extras/models/models.py:236 msgid "webhook" msgstr "" -#: netbox/extras/models/models.py:235 +#: extras/models/models.py:237 msgid "webhooks" msgstr "" -#: netbox/extras/models/models.py:253 +#: extras/models/models.py:255 msgid "Do not specify a CA certificate file if SSL verification is disabled." msgstr "" -#: netbox/extras/models/models.py:293 +#: extras/models/models.py:295 msgid "The object type(s) to which this link applies." msgstr "" -#: netbox/extras/models/models.py:305 +#: extras/models/models.py:307 msgid "link text" msgstr "" -#: netbox/extras/models/models.py:306 +#: extras/models/models.py:308 msgid "Jinja2 template code for link text" msgstr "" -#: netbox/extras/models/models.py:309 +#: extras/models/models.py:311 msgid "link URL" msgstr "" -#: netbox/extras/models/models.py:310 +#: extras/models/models.py:312 msgid "Jinja2 template code for link URL" msgstr "" -#: netbox/extras/models/models.py:320 +#: extras/models/models.py:322 msgid "Links with the same group will appear as a dropdown menu" msgstr "" -#: netbox/extras/models/models.py:330 +#: extras/models/models.py:332 msgid "new window" msgstr "" -#: netbox/extras/models/models.py:332 +#: extras/models/models.py:334 msgid "Force link to open in a new window" msgstr "" -#: netbox/extras/models/models.py:341 +#: extras/models/models.py:343 msgid "custom link" msgstr "" -#: netbox/extras/models/models.py:342 +#: extras/models/models.py:344 msgid "custom links" msgstr "" -#: netbox/extras/models/models.py:389 +#: extras/models/models.py:391 msgid "The object type(s) to which this template applies." msgstr "" -#: netbox/extras/models/models.py:402 -msgid "" -"Jinja2 template code. The list of objects being exported is passed as a " -"context variable named queryset." -msgstr "" - -#: netbox/extras/models/models.py:410 -msgid "Defaults to text/plain; charset=utf-8" -msgstr "" - -#: netbox/extras/models/models.py:413 -msgid "file extension" -msgstr "" - -#: netbox/extras/models/models.py:416 -msgid "Extension to append to the rendered filename" -msgstr "" - -#: netbox/extras/models/models.py:419 -msgid "as attachment" -msgstr "" - -#: netbox/extras/models/models.py:421 -msgid "Download file as attachment" -msgstr "" - -#: netbox/extras/models/models.py:430 +#: extras/models/models.py:409 msgid "export template" msgstr "" -#: netbox/extras/models/models.py:431 +#: extras/models/models.py:410 msgid "export templates" msgstr "" -#: netbox/extras/models/models.py:448 +#: extras/models/models.py:427 #, python-brace-format msgid "\"{name}\" is a reserved name. Please choose a different name." msgstr "" -#: netbox/extras/models/models.py:498 +#: extras/models/models.py:456 msgid "The object type(s) to which this filter applies." msgstr "" -#: netbox/extras/models/models.py:530 +#: extras/models/models.py:488 extras/models/models.py:567 msgid "shared" msgstr "" -#: netbox/extras/models/models.py:543 +#: extras/models/models.py:501 msgid "saved filter" msgstr "" -#: netbox/extras/models/models.py:544 +#: extras/models/models.py:502 msgid "saved filters" msgstr "" -#: netbox/extras/models/models.py:562 +#: extras/models/models.py:520 msgid "Filter parameters must be stored as a dictionary of keyword arguments." msgstr "" -#: netbox/extras/models/models.py:590 +#: extras/models/models.py:537 +msgid "The table's object type" +msgstr "" + +#: extras/models/models.py:540 +msgid "table" +msgstr "" + +#: extras/models/models.py:583 +msgid "table config" +msgstr "" + +#: extras/models/models.py:584 +msgid "table configs" +msgstr "" + +#: extras/models/models.py:622 +#, python-brace-format +msgid "Unknown table: {name}" +msgstr "" + +#: extras/models/models.py:633 extras/models/models.py:640 +#, python-brace-format +msgid "Unknown column: {name}" +msgstr "" + +#: extras/models/models.py:663 msgid "image height" msgstr "" -#: netbox/extras/models/models.py:593 +#: extras/models/models.py:666 msgid "image width" msgstr "" -#: netbox/extras/models/models.py:610 +#: extras/models/models.py:683 msgid "image attachment" msgstr "" -#: netbox/extras/models/models.py:611 +#: extras/models/models.py:684 msgid "image attachments" msgstr "" -#: netbox/extras/models/models.py:625 +#: extras/models/models.py:698 #, python-brace-format msgid "Image attachments cannot be assigned to this object type ({type})." msgstr "" -#: netbox/extras/models/models.py:688 +#: extras/models/models.py:761 msgid "kind" msgstr "" -#: netbox/extras/models/models.py:702 +#: extras/models/models.py:775 msgid "journal entry" msgstr "" -#: netbox/extras/models/models.py:703 +#: extras/models/models.py:776 msgid "journal entries" msgstr "" -#: netbox/extras/models/models.py:721 +#: extras/models/models.py:794 #, python-brace-format msgid "Journaling is not supported for this object type ({type})." msgstr "" -#: netbox/extras/models/models.py:763 +#: extras/models/models.py:836 msgid "bookmark" msgstr "" -#: netbox/extras/models/models.py:764 +#: extras/models/models.py:837 msgid "bookmarks" msgstr "" -#: netbox/extras/models/models.py:777 +#: extras/models/models.py:850 #, python-brace-format msgid "Bookmarks cannot be assigned to this object type ({type})." msgstr "" -#: netbox/extras/models/notifications.py:43 +#: extras/models/notifications.py:43 msgid "read" msgstr "" -#: netbox/extras/models/notifications.py:66 +#: extras/models/notifications.py:66 msgid "event" msgstr "" -#: netbox/extras/models/notifications.py:84 +#: extras/models/notifications.py:84 msgid "notification" msgstr "" -#: netbox/extras/models/notifications.py:85 +#: extras/models/notifications.py:85 msgid "notifications" msgstr "" -#: netbox/extras/models/notifications.py:99 -#: netbox/extras/models/notifications.py:234 +#: extras/models/notifications.py:99 extras/models/notifications.py:234 #, python-brace-format msgid "Objects of this type ({type}) do not support notifications." msgstr "" -#: netbox/extras/models/notifications.py:137 netbox/users/models/users.py:58 -#: netbox/users/models/users.py:77 +#: extras/models/notifications.py:137 users/models/users.py:58 +#: users/models/users.py:77 msgid "groups" msgstr "" -#: netbox/extras/models/notifications.py:143 netbox/users/models/users.py:93 +#: extras/models/notifications.py:143 users/models/users.py:93 msgid "users" msgstr "" -#: netbox/extras/models/notifications.py:152 +#: extras/models/notifications.py:152 msgid "notification group" msgstr "" -#: netbox/extras/models/notifications.py:153 +#: extras/models/notifications.py:153 msgid "notification groups" msgstr "" -#: netbox/extras/models/notifications.py:217 +#: extras/models/notifications.py:217 msgid "subscription" msgstr "" -#: netbox/extras/models/notifications.py:218 +#: extras/models/notifications.py:218 msgid "subscriptions" msgstr "" -#: netbox/extras/models/scripts.py:42 +#: extras/models/scripts.py:42 msgid "is executable" msgstr "" -#: netbox/extras/models/scripts.py:64 +#: extras/models/scripts.py:64 msgid "script" msgstr "" -#: netbox/extras/models/scripts.py:65 +#: extras/models/scripts.py:65 msgid "scripts" msgstr "" -#: netbox/extras/models/scripts.py:111 +#: extras/models/scripts.py:111 msgid "script module" msgstr "" -#: netbox/extras/models/scripts.py:112 +#: extras/models/scripts.py:112 msgid "script modules" msgstr "" -#: netbox/extras/models/search.py:22 +#: extras/models/search.py:22 msgid "timestamp" msgstr "" -#: netbox/extras/models/search.py:37 +#: extras/models/search.py:37 msgid "field" msgstr "" -#: netbox/extras/models/search.py:45 +#: extras/models/search.py:45 msgid "value" msgstr "" -#: netbox/extras/models/search.py:56 +#: extras/models/search.py:56 msgid "cached value" msgstr "" -#: netbox/extras/models/search.py:57 +#: extras/models/search.py:57 msgid "cached values" msgstr "" -#: netbox/extras/models/staging.py:45 -msgid "branch" -msgstr "" - -#: netbox/extras/models/staging.py:46 -msgid "branches" -msgstr "" - -#: netbox/extras/models/staging.py:105 -msgid "staged change" -msgstr "" - -#: netbox/extras/models/staging.py:106 -msgid "staged changes" -msgstr "" - -#: netbox/extras/models/tags.py:40 +#: extras/models/tags.py:41 msgid "The object type(s) to which this tag can be applied." msgstr "" -#: netbox/extras/models/tags.py:49 +#: extras/models/tags.py:54 msgid "tag" msgstr "" -#: netbox/extras/models/tags.py:50 +#: extras/models/tags.py:55 msgid "tags" msgstr "" -#: netbox/extras/models/tags.py:78 +#: extras/models/tags.py:84 msgid "tagged item" msgstr "" -#: netbox/extras/models/tags.py:79 +#: extras/models/tags.py:85 msgid "tagged items" msgstr "" -#: netbox/extras/scripts.py:432 +#: extras/scripts.py:471 msgid "Script Data" msgstr "" -#: netbox/extras/scripts.py:436 +#: extras/scripts.py:475 msgid "Script Execution Parameters" msgstr "" -#: netbox/extras/tables/columns.py:12 -#: netbox/templates/htmx/notifications.html:18 +#: extras/scripts.py:572 +msgid "load_yaml is deprecated and will be removed in v4.4" +msgstr "" + +#: extras/scripts.py:587 +msgid "load_json is deprecated and will be removed in v4.4" +msgstr "" + +#: extras/tables/columns.py:12 templates/htmx/notifications.html:18 msgid "Dismiss" msgstr "" -#: netbox/extras/tables/tables.py:65 netbox/extras/tables/tables.py:162 -#: netbox/extras/tables/tables.py:187 netbox/extras/tables/tables.py:253 -#: netbox/extras/tables/tables.py:279 netbox/extras/tables/tables.py:415 -#: netbox/extras/tables/tables.py:449 -#: netbox/templates/extras/customfield.html:105 -#: netbox/templates/extras/eventrule.html:27 -#: netbox/templates/users/objectpermission.html:64 netbox/users/tables.py:80 +#: extras/tables/tables.py:66 extras/tables/tables.py:163 +#: extras/tables/tables.py:188 extras/tables/tables.py:264 +#: extras/tables/tables.py:320 extras/tables/tables.py:456 +#: extras/tables/tables.py:490 templates/extras/customfield.html:105 +#: templates/extras/eventrule.html:27 templates/users/objectpermission.html:64 +#: users/tables.py:80 msgid "Object Types" msgstr "" -#: netbox/extras/tables/tables.py:72 +#: extras/tables/tables.py:73 msgid "Validate Uniqueness" msgstr "" -#: netbox/extras/tables/tables.py:76 +#: extras/tables/tables.py:77 msgid "Visible" msgstr "" -#: netbox/extras/tables/tables.py:79 +#: extras/tables/tables.py:80 msgid "Editable" msgstr "" -#: netbox/extras/tables/tables.py:85 +#: extras/tables/tables.py:86 msgid "Related Object Type" msgstr "" -#: netbox/extras/tables/tables.py:89 -#: netbox/templates/extras/customfield.html:51 +#: extras/tables/tables.py:90 templates/extras/customfield.html:51 msgid "Choice Set" msgstr "" -#: netbox/extras/tables/tables.py:97 +#: extras/tables/tables.py:98 msgid "Is Cloneable" msgstr "" -#: netbox/extras/tables/tables.py:101 -#: netbox/templates/extras/customfield.html:118 +#: extras/tables/tables.py:102 templates/extras/customfield.html:118 msgid "Minimum Value" msgstr "" -#: netbox/extras/tables/tables.py:104 -#: netbox/templates/extras/customfield.html:122 +#: extras/tables/tables.py:105 templates/extras/customfield.html:122 msgid "Maximum Value" msgstr "" -#: netbox/extras/tables/tables.py:107 +#: extras/tables/tables.py:108 msgid "Validation Regex" msgstr "" -#: netbox/extras/tables/tables.py:140 +#: extras/tables/tables.py:141 msgid "Count" msgstr "" -#: netbox/extras/tables/tables.py:143 +#: extras/tables/tables.py:144 msgid "Order Alphabetically" msgstr "" -#: netbox/extras/tables/tables.py:168 -#: netbox/templates/extras/customlink.html:33 +#: extras/tables/tables.py:169 templates/extras/customlink.html:33 msgid "New Window" msgstr "" -#: netbox/extras/tables/tables.py:190 +#: extras/tables/tables.py:191 extras/tables/tables.py:577 +#: templates/extras/configtemplate.html:21 +#: templates/extras/exporttemplate.html:28 +msgid "MIME Type" +msgstr "" + +#: extras/tables/tables.py:194 extras/tables/tables.py:580 +#: templates/extras/configtemplate.html:25 +#: templates/extras/exporttemplate.html:32 +msgid "File Name" +msgstr "" + +#: extras/tables/tables.py:197 extras/tables/tables.py:583 +#: templates/extras/configtemplate.html:29 +#: templates/extras/exporttemplate.html:36 +msgid "File Extension" +msgstr "" + +#: extras/tables/tables.py:200 extras/tables/tables.py:586 msgid "As Attachment" msgstr "" -#: netbox/extras/tables/tables.py:198 netbox/extras/tables/tables.py:490 -#: netbox/extras/tables/tables.py:528 netbox/templates/core/datafile.html:24 -#: netbox/templates/extras/configcontext.html:39 -#: netbox/templates/extras/configtemplate.html:31 -#: netbox/templates/extras/exporttemplate.html:45 -#: netbox/templates/extras/object_render_config.html:23 -#: netbox/templates/generic/bulk_import.html:35 +#: extras/tables/tables.py:208 extras/tables/tables.py:531 +#: extras/tables/tables.py:569 templates/core/datafile.html:24 +#: templates/extras/configcontext.html:39 +#: templates/extras/configtemplate.html:47 +#: templates/extras/exporttemplate.html:54 +#: templates/extras/object_render_config.html:23 +#: templates/generic/bulk_import.html:35 msgid "Data File" msgstr "" -#: netbox/extras/tables/tables.py:203 netbox/extras/tables/tables.py:502 -#: netbox/extras/tables/tables.py:533 +#: extras/tables/tables.py:213 extras/tables/tables.py:543 +#: extras/tables/tables.py:574 msgid "Synced" msgstr "" -#: netbox/extras/tables/tables.py:230 +#: extras/tables/tables.py:241 msgid "Image" msgstr "" -#: netbox/extras/tables/tables.py:235 +#: extras/tables/tables.py:246 msgid "Size (Bytes)" msgstr "" -#: netbox/extras/tables/tables.py:342 +#: extras/tables/tables.py:297 +msgid "Table Name" +msgstr "" + +#: extras/tables/tables.py:383 msgid "Read" msgstr "" -#: netbox/extras/tables/tables.py:385 +#: extras/tables/tables.py:426 msgid "SSL Validation" msgstr "" -#: netbox/extras/tables/tables.py:421 netbox/templates/extras/eventrule.html:37 +#: extras/tables/tables.py:462 templates/extras/eventrule.html:37 msgid "Event Types" msgstr "" -#: netbox/extras/tables/tables.py:541 netbox/netbox/navigation/menu.py:77 -#: netbox/templates/dcim/devicerole.html:8 +#: extras/tables/tables.py:595 netbox/navigation/menu.py:77 +#: templates/dcim/devicerole.html:8 msgid "Device Roles" msgstr "" -#: netbox/extras/tables/tables.py:593 +#: extras/tables/tables.py:648 msgid "Comments (Short)" msgstr "" -#: netbox/extras/tables/tables.py:612 netbox/extras/tables/tables.py:663 +#: extras/tables/tables.py:667 extras/tables/tables.py:718 msgid "Line" msgstr "" -#: netbox/extras/tables/tables.py:619 netbox/extras/tables/tables.py:673 +#: extras/tables/tables.py:674 extras/tables/tables.py:728 msgid "Level" msgstr "" -#: netbox/extras/tables/tables.py:625 netbox/extras/tables/tables.py:682 +#: extras/tables/tables.py:680 extras/tables/tables.py:737 msgid "Message" msgstr "" -#: netbox/extras/tables/tables.py:666 +#: extras/tables/tables.py:721 msgid "Method" msgstr "" -#: netbox/extras/validators.py:15 +#: extras/validators.py:15 #, python-format msgid "Ensure this value is equal to %(limit_value)s." msgstr "" -#: netbox/extras/validators.py:26 +#: extras/validators.py:26 #, python-format msgid "Ensure this value does not equal %(limit_value)s." msgstr "" -#: netbox/extras/validators.py:37 +#: extras/validators.py:37 msgid "This field must be empty." msgstr "" -#: netbox/extras/validators.py:52 +#: extras/validators.py:52 msgid "This field must not be empty." msgstr "" -#: netbox/extras/validators.py:94 +#: extras/validators.py:94 msgid "Validation rules must be passed as a dictionary" msgstr "" -#: netbox/extras/validators.py:119 +#: extras/validators.py:119 #, python-brace-format msgid "Custom validation failed for {attribute}: {exception}" msgstr "" -#: netbox/extras/validators.py:133 +#: extras/validators.py:133 #, python-brace-format msgid "Invalid attribute \"{name}\" for request" msgstr "" -#: netbox/extras/validators.py:150 +#: extras/validators.py:150 #, python-brace-format msgid "Invalid attribute \"{name}\" for {model}" msgstr "" -#: netbox/extras/views.py:932 +#: extras/views.py:974 #, python-brace-format msgid "An error occurred while rendering the template: {error}" msgstr "" -#: netbox/extras/views.py:1084 +#: extras/views.py:1126 msgid "Your dashboard has been reset." msgstr "" -#: netbox/extras/views.py:1130 +#: extras/views.py:1172 msgid "Added widget: " msgstr "" -#: netbox/extras/views.py:1171 +#: extras/views.py:1213 msgid "Updated widget: " msgstr "" -#: netbox/extras/views.py:1207 +#: extras/views.py:1249 msgid "Deleted widget: " msgstr "" -#: netbox/extras/views.py:1209 +#: extras/views.py:1251 msgid "Error deleting widget: " msgstr "" -#: netbox/extras/views.py:1307 +#: extras/views.py:1349 msgid "Unable to run script: RQ worker process not running." msgstr "" -#: netbox/ipam/api/field_serializers.py:17 +#: ipam/api/field_serializers.py:17 msgid "Enter a valid IPv4 or IPv6 address with optional mask." msgstr "" -#: netbox/ipam/api/field_serializers.py:24 +#: ipam/api/field_serializers.py:24 #, python-brace-format msgid "Invalid IP address format: {data}" msgstr "" -#: netbox/ipam/api/field_serializers.py:37 +#: ipam/api/field_serializers.py:37 msgid "Enter a valid IPv4 or IPv6 prefix and mask in CIDR notation." msgstr "" -#: netbox/ipam/api/field_serializers.py:44 +#: ipam/api/field_serializers.py:44 #, python-brace-format msgid "Invalid IP prefix format: {data}" msgstr "" -#: netbox/ipam/api/views.py:370 +#: ipam/api/views.py:370 msgid "" "Insufficient space is available to accommodate the requested prefix size(s)" msgstr "" -#: netbox/ipam/choices.py:30 +#: ipam/choices.py:30 msgid "Container" msgstr "" -#: netbox/ipam/choices.py:72 +#: ipam/choices.py:72 msgid "DHCP" msgstr "" -#: netbox/ipam/choices.py:73 +#: ipam/choices.py:73 msgid "SLAAC" msgstr "" -#: netbox/ipam/choices.py:89 +#: ipam/choices.py:89 msgid "Loopback" msgstr "" -#: netbox/ipam/choices.py:91 +#: ipam/choices.py:91 msgid "Anycast" msgstr "" -#: netbox/ipam/choices.py:115 +#: ipam/choices.py:115 msgid "Standard" msgstr "" -#: netbox/ipam/choices.py:120 +#: ipam/choices.py:120 msgid "CheckPoint" msgstr "" -#: netbox/ipam/choices.py:123 +#: ipam/choices.py:123 msgid "Cisco" msgstr "" -#: netbox/ipam/choices.py:137 +#: ipam/choices.py:137 msgid "Plaintext" msgstr "" -#: netbox/ipam/choices.py:166 netbox/ipam/forms/model_forms.py:799 -#: netbox/ipam/forms/model_forms.py:827 netbox/templates/ipam/service.html:21 +#: ipam/choices.py:166 ipam/forms/model_forms.py:797 +#: ipam/forms/model_forms.py:851 templates/ipam/service.html:23 msgid "Service" msgstr "" -#: netbox/ipam/choices.py:167 +#: ipam/choices.py:167 msgid "Customer" msgstr "" -#: netbox/ipam/fields.py:36 +#: ipam/fields.py:36 #, python-brace-format msgid "Invalid IP address format: {address}" msgstr "" -#: netbox/ipam/filtersets.py:52 netbox/vpn/filtersets.py:304 +#: ipam/filtersets.py:52 vpn/filtersets.py:307 msgid "Import target" msgstr "" -#: netbox/ipam/filtersets.py:58 netbox/vpn/filtersets.py:310 +#: ipam/filtersets.py:58 vpn/filtersets.py:313 msgid "Import target (name)" msgstr "" -#: netbox/ipam/filtersets.py:63 netbox/vpn/filtersets.py:315 +#: ipam/filtersets.py:63 vpn/filtersets.py:318 msgid "Export target" msgstr "" -#: netbox/ipam/filtersets.py:69 netbox/vpn/filtersets.py:321 +#: ipam/filtersets.py:69 vpn/filtersets.py:324 msgid "Export target (name)" msgstr "" -#: netbox/ipam/filtersets.py:90 +#: ipam/filtersets.py:90 msgid "Importing VRF" msgstr "" -#: netbox/ipam/filtersets.py:96 +#: ipam/filtersets.py:96 msgid "Import VRF (RD)" msgstr "" -#: netbox/ipam/filtersets.py:101 +#: ipam/filtersets.py:101 msgid "Exporting VRF" msgstr "" -#: netbox/ipam/filtersets.py:107 +#: ipam/filtersets.py:107 msgid "Export VRF (RD)" msgstr "" -#: netbox/ipam/filtersets.py:112 +#: ipam/filtersets.py:112 msgid "Importing L2VPN" msgstr "" -#: netbox/ipam/filtersets.py:118 +#: ipam/filtersets.py:118 msgid "Importing L2VPN (identifier)" msgstr "" -#: netbox/ipam/filtersets.py:123 +#: ipam/filtersets.py:123 msgid "Exporting L2VPN" msgstr "" -#: netbox/ipam/filtersets.py:129 +#: ipam/filtersets.py:129 msgid "Exporting L2VPN (identifier)" msgstr "" -#: netbox/ipam/filtersets.py:159 netbox/ipam/filtersets.py:300 -#: netbox/ipam/forms/model_forms.py:229 netbox/ipam/tables/ip.py:158 -#: netbox/templates/ipam/prefix.html:12 +#: ipam/filtersets.py:159 ipam/filtersets.py:300 ipam/forms/model_forms.py:229 +#: ipam/tables/ip.py:159 templates/ipam/prefix.html:12 msgid "Prefix" msgstr "" -#: netbox/ipam/filtersets.py:163 netbox/ipam/filtersets.py:202 -#: netbox/ipam/filtersets.py:227 +#: ipam/filtersets.py:163 ipam/filtersets.py:202 ipam/filtersets.py:227 msgid "RIR (ID)" msgstr "" -#: netbox/ipam/filtersets.py:169 netbox/ipam/filtersets.py:208 -#: netbox/ipam/filtersets.py:233 +#: ipam/filtersets.py:169 ipam/filtersets.py:208 ipam/filtersets.py:233 msgid "RIR (slug)" msgstr "" -#: netbox/ipam/filtersets.py:304 +#: ipam/filtersets.py:304 msgid "Within prefix" msgstr "" -#: netbox/ipam/filtersets.py:308 +#: ipam/filtersets.py:308 msgid "Within and including prefix" msgstr "" -#: netbox/ipam/filtersets.py:312 +#: ipam/filtersets.py:312 msgid "Prefixes which contain this prefix or IP" msgstr "" -#: netbox/ipam/filtersets.py:323 netbox/ipam/filtersets.py:567 -#: netbox/ipam/forms/bulk_edit.py:327 netbox/ipam/forms/filtersets.py:205 -#: netbox/ipam/forms/filtersets.py:348 +#: ipam/filtersets.py:323 ipam/filtersets.py:567 ipam/forms/bulk_edit.py:332 +#: ipam/forms/filtersets.py:205 ipam/forms/filtersets.py:355 msgid "Mask length" msgstr "" -#: netbox/ipam/filtersets.py:358 +#: ipam/filtersets.py:358 msgid "VLAN Group (ID)" msgstr "" -#: netbox/ipam/filtersets.py:364 +#: ipam/filtersets.py:364 msgid "VLAN Group (slug)" msgstr "" -#: netbox/ipam/filtersets.py:368 netbox/vpn/filtersets.py:427 +#: ipam/filtersets.py:368 vpn/filtersets.py:430 msgid "VLAN (ID)" msgstr "" -#: netbox/ipam/filtersets.py:372 netbox/vpn/filtersets.py:422 +#: ipam/filtersets.py:372 vpn/filtersets.py:425 msgid "VLAN number (1-4094)" msgstr "" -#: netbox/ipam/filtersets.py:466 netbox/ipam/filtersets.py:470 -#: netbox/ipam/filtersets.py:562 netbox/ipam/forms/model_forms.py:506 -#: netbox/templates/tenancy/contact.html:53 -#: netbox/tenancy/forms/bulk_edit.py:113 +#: ipam/filtersets.py:466 ipam/filtersets.py:470 ipam/filtersets.py:562 +#: ipam/forms/model_forms.py:506 templates/tenancy/contact.html:63 +#: tenancy/forms/bulk_edit.py:120 msgid "Address" msgstr "" -#: netbox/ipam/filtersets.py:474 +#: ipam/filtersets.py:474 msgid "Ranges which contain this prefix or IP" msgstr "" -#: netbox/ipam/filtersets.py:502 netbox/ipam/filtersets.py:558 +#: ipam/filtersets.py:502 ipam/filtersets.py:558 msgid "Parent prefix" msgstr "" -#: netbox/ipam/filtersets.py:643 +#: ipam/filtersets.py:643 msgid "FHRP group (ID)" msgstr "" -#: netbox/ipam/filtersets.py:647 +#: ipam/filtersets.py:647 msgid "Is assigned to an interface" msgstr "" -#: netbox/ipam/filtersets.py:651 +#: ipam/filtersets.py:651 msgid "Is assigned" msgstr "" -#: netbox/ipam/filtersets.py:663 +#: ipam/filtersets.py:663 msgid "Service (ID)" msgstr "" -#: netbox/ipam/filtersets.py:668 +#: ipam/filtersets.py:668 msgid "NAT inside IP address (ID)" msgstr "" -#: netbox/ipam/filtersets.py:1027 +#: ipam/filtersets.py:1027 msgid "Q-in-Q SVLAN (ID)" msgstr "" -#: netbox/ipam/filtersets.py:1031 +#: ipam/filtersets.py:1031 msgid "Q-in-Q SVLAN number (1-4094)" msgstr "" -#: netbox/ipam/filtersets.py:1052 +#: ipam/filtersets.py:1052 msgid "Assigned VM interface" msgstr "" -#: netbox/ipam/filtersets.py:1123 +#: ipam/filtersets.py:1123 msgid "VLAN Translation Policy (name)" msgstr "" -#: netbox/ipam/filtersets.py:1189 +#: ipam/filtersets.py:1189 +msgid "FHRP Group (name)" +msgstr "" + +#: ipam/filtersets.py:1194 +msgid "FHRP Group (ID)" +msgstr "" + +#: ipam/filtersets.py:1199 msgid "IP address (ID)" msgstr "" -#: netbox/ipam/filtersets.py:1195 netbox/ipam/models/ip.py:788 +#: ipam/filtersets.py:1205 ipam/models/ip.py:798 msgid "IP address" msgstr "" -#: netbox/ipam/filtersets.py:1220 +#: ipam/filtersets.py:1257 msgid "Primary IPv4 (ID)" msgstr "" -#: netbox/ipam/filtersets.py:1226 +#: ipam/filtersets.py:1263 msgid "Primary IPv4 (address)" msgstr "" -#: netbox/ipam/filtersets.py:1231 +#: ipam/filtersets.py:1268 msgid "Primary IPv6 (ID)" msgstr "" -#: netbox/ipam/filtersets.py:1237 +#: ipam/filtersets.py:1274 msgid "Primary IPv6 (address)" msgstr "" -#: netbox/ipam/formfields.py:14 +#: ipam/formfields.py:14 msgid "Enter a valid IPv4 or IPv6 address (without a mask)." msgstr "" -#: netbox/ipam/formfields.py:32 +#: ipam/formfields.py:32 #, python-brace-format msgid "Invalid IPv4/IPv6 address format: {address}" msgstr "" -#: netbox/ipam/formfields.py:37 +#: ipam/formfields.py:37 msgid "This field requires an IP address without a mask." msgstr "" -#: netbox/ipam/formfields.py:39 netbox/ipam/formfields.py:61 +#: ipam/formfields.py:39 ipam/formfields.py:61 msgid "Please specify a valid IPv4 or IPv6 address." msgstr "" -#: netbox/ipam/formfields.py:44 +#: ipam/formfields.py:44 msgid "Enter a valid IPv4 or IPv6 address (with CIDR mask)." msgstr "" -#: netbox/ipam/formfields.py:56 +#: ipam/formfields.py:56 msgid "CIDR mask (e.g. /24) is required." msgstr "" -#: netbox/ipam/forms/bulk_create.py:13 +#: ipam/forms/bulk_create.py:13 msgid "Address pattern" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:53 +#: ipam/forms/bulk_edit.py:53 msgid "Enforce unique space" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:91 +#: ipam/forms/bulk_edit.py:91 msgid "Is private" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:112 netbox/ipam/forms/bulk_edit.py:141 -#: netbox/ipam/forms/bulk_edit.py:166 netbox/ipam/forms/bulk_import.py:92 -#: netbox/ipam/forms/bulk_import.py:112 netbox/ipam/forms/bulk_import.py:132 -#: netbox/ipam/forms/filtersets.py:113 netbox/ipam/forms/filtersets.py:128 -#: netbox/ipam/forms/filtersets.py:151 netbox/ipam/forms/model_forms.py:99 -#: netbox/ipam/forms/model_forms.py:112 netbox/ipam/forms/model_forms.py:135 -#: netbox/ipam/forms/model_forms.py:154 netbox/ipam/models/asns.py:31 -#: netbox/ipam/models/asns.py:100 netbox/ipam/models/ip.py:71 -#: netbox/ipam/models/ip.py:87 netbox/ipam/tables/asn.py:20 -#: netbox/ipam/tables/asn.py:45 netbox/templates/ipam/aggregate.html:18 -#: netbox/templates/ipam/asn.html:27 netbox/templates/ipam/asnrange.html:19 -#: netbox/templates/ipam/rir.html:19 +#: ipam/forms/bulk_edit.py:112 ipam/forms/bulk_edit.py:141 +#: ipam/forms/bulk_edit.py:166 ipam/forms/bulk_import.py:92 +#: ipam/forms/bulk_import.py:112 ipam/forms/bulk_import.py:132 +#: ipam/forms/filtersets.py:113 ipam/forms/filtersets.py:128 +#: ipam/forms/filtersets.py:151 ipam/forms/model_forms.py:99 +#: ipam/forms/model_forms.py:112 ipam/forms/model_forms.py:135 +#: ipam/forms/model_forms.py:154 ipam/models/asns.py:31 ipam/models/asns.py:100 +#: ipam/models/ip.py:71 ipam/models/ip.py:87 ipam/tables/asn.py:20 +#: ipam/tables/asn.py:45 templates/ipam/aggregate.html:18 +#: templates/ipam/asn.html:27 templates/ipam/asnrange.html:19 +#: templates/ipam/rir.html:19 msgid "RIR" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:174 +#: ipam/forms/bulk_edit.py:174 msgid "Date added" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:213 netbox/ipam/forms/filtersets.py:266 -#: netbox/ipam/forms/model_forms.py:628 netbox/ipam/forms/model_forms.py:675 -#: netbox/ipam/tables/ip.py:201 netbox/templates/ipam/vlan_edit.html:49 -#: netbox/templates/ipam/vlangroup.html:27 +#: ipam/forms/bulk_edit.py:213 ipam/forms/filtersets.py:266 +#: ipam/forms/model_forms.py:628 ipam/forms/model_forms.py:676 +#: ipam/tables/ip.py:202 templates/ipam/vlan_edit.html:49 +#: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:218 netbox/ipam/forms/bulk_import.py:188 -#: netbox/ipam/forms/filtersets.py:271 netbox/ipam/forms/model_forms.py:217 -#: netbox/ipam/models/vlans.py:273 netbox/ipam/tables/ip.py:206 -#: netbox/templates/ipam/prefix.html:56 netbox/templates/ipam/vlan.html:12 -#: netbox/templates/ipam/vlan/base.html:6 -#: netbox/templates/ipam/vlan_edit.html:14 -#: netbox/templates/wireless/wirelesslan.html:38 -#: netbox/vpn/forms/bulk_import.py:304 netbox/vpn/forms/filtersets.py:290 -#: netbox/vpn/forms/model_forms.py:436 netbox/vpn/forms/model_forms.py:455 -#: netbox/wireless/forms/bulk_edit.py:57 -#: netbox/wireless/forms/bulk_import.py:50 -#: netbox/wireless/forms/model_forms.py:50 netbox/wireless/models.py:102 +#: ipam/forms/bulk_edit.py:218 ipam/forms/bulk_import.py:188 +#: ipam/forms/filtersets.py:271 ipam/forms/model_forms.py:217 +#: ipam/models/vlans.py:279 ipam/tables/ip.py:207 templates/ipam/prefix.html:56 +#: templates/ipam/vlan.html:12 templates/ipam/vlan/base.html:6 +#: templates/ipam/vlan_edit.html:14 templates/wireless/wirelesslan.html:38 +#: vpn/forms/bulk_import.py:309 vpn/forms/filtersets.py:295 +#: vpn/forms/model_forms.py:436 vpn/forms/model_forms.py:455 +#: wireless/forms/bulk_edit.py:58 wireless/forms/bulk_import.py:50 +#: wireless/forms/model_forms.py:51 wireless/models.py:102 msgid "VLAN" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:229 +#: ipam/forms/bulk_edit.py:229 msgid "Prefix length" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:252 netbox/ipam/forms/filtersets.py:251 -#: netbox/templates/ipam/prefix.html:81 +#: ipam/forms/bulk_edit.py:252 ipam/forms/filtersets.py:251 +#: templates/ipam/prefix.html:81 msgid "Is a pool" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:257 netbox/ipam/forms/bulk_edit.py:302 -#: netbox/ipam/forms/filtersets.py:258 netbox/ipam/forms/filtersets.py:309 -#: netbox/ipam/models/ip.py:256 netbox/ipam/models/ip.py:525 +#: ipam/forms/bulk_edit.py:257 ipam/forms/bulk_edit.py:307 +#: ipam/forms/filtersets.py:258 ipam/forms/filtersets.py:316 +#: ipam/models/ip.py:256 msgid "Treat as fully utilized" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:271 netbox/ipam/forms/filtersets.py:179 -#: netbox/ipam/forms/model_forms.py:232 +#: ipam/forms/bulk_edit.py:271 ipam/forms/filtersets.py:179 +#: ipam/forms/model_forms.py:232 msgid "VLAN Assignment" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:350 netbox/ipam/models/ip.py:772 +#: ipam/forms/bulk_edit.py:302 ipam/forms/filtersets.py:309 +msgid "Treat as populated" +msgstr "" + +#: ipam/forms/bulk_edit.py:355 ipam/models/ip.py:782 msgid "DNS name" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:371 netbox/ipam/forms/bulk_edit.py:562 -#: netbox/ipam/forms/bulk_import.py:433 netbox/ipam/forms/bulk_import.py:544 -#: netbox/ipam/forms/bulk_import.py:570 netbox/ipam/forms/filtersets.py:407 -#: netbox/ipam/forms/filtersets.py:596 netbox/templates/ipam/fhrpgroup.html:22 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:24 -#: netbox/templates/ipam/service.html:32 -#: netbox/templates/ipam/servicetemplate.html:19 +#: ipam/forms/bulk_edit.py:376 ipam/forms/bulk_edit.py:573 +#: ipam/forms/bulk_import.py:433 ipam/forms/bulk_import.py:551 +#: ipam/forms/bulk_import.py:579 ipam/forms/filtersets.py:414 +#: ipam/forms/filtersets.py:604 templates/ipam/fhrpgroup.html:22 +#: templates/ipam/inc/panels/fhrp_groups.html:24 templates/ipam/service.html:34 +#: templates/ipam/servicetemplate.html:19 msgid "Protocol" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:378 netbox/ipam/forms/filtersets.py:414 -#: netbox/ipam/tables/fhrp.py:22 netbox/templates/ipam/fhrpgroup.html:26 +#: ipam/forms/bulk_edit.py:383 ipam/forms/filtersets.py:421 +#: ipam/tables/fhrp.py:22 templates/ipam/fhrpgroup.html:26 msgid "Group ID" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:383 netbox/ipam/forms/filtersets.py:419 -#: netbox/wireless/forms/bulk_edit.py:70 netbox/wireless/forms/bulk_edit.py:118 -#: netbox/wireless/forms/bulk_import.py:64 -#: netbox/wireless/forms/bulk_import.py:67 -#: netbox/wireless/forms/bulk_import.py:109 -#: netbox/wireless/forms/bulk_import.py:112 -#: netbox/wireless/forms/filtersets.py:57 -#: netbox/wireless/forms/filtersets.py:116 +#: ipam/forms/bulk_edit.py:388 ipam/forms/filtersets.py:426 +#: wireless/forms/bulk_edit.py:71 wireless/forms/bulk_edit.py:119 +#: wireless/forms/bulk_import.py:64 wireless/forms/bulk_import.py:67 +#: wireless/forms/bulk_import.py:109 wireless/forms/bulk_import.py:112 +#: wireless/forms/filtersets.py:57 wireless/forms/filtersets.py:116 msgid "Authentication type" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:388 netbox/ipam/forms/filtersets.py:423 +#: ipam/forms/bulk_edit.py:393 ipam/forms/filtersets.py:430 msgid "Authentication key" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:405 netbox/ipam/forms/filtersets.py:400 -#: netbox/ipam/forms/model_forms.py:517 netbox/netbox/navigation/menu.py:407 -#: netbox/templates/ipam/fhrpgroup.html:49 -#: netbox/templates/wireless/inc/authentication_attrs.html:5 -#: netbox/wireless/forms/bulk_edit.py:94 netbox/wireless/forms/bulk_edit.py:152 -#: netbox/wireless/forms/filtersets.py:39 -#: netbox/wireless/forms/filtersets.py:104 -#: netbox/wireless/forms/model_forms.py:58 -#: netbox/wireless/forms/model_forms.py:174 +#: ipam/forms/bulk_edit.py:410 ipam/forms/filtersets.py:407 +#: ipam/forms/model_forms.py:517 netbox/navigation/menu.py:409 +#: templates/ipam/fhrpgroup.html:49 +#: templates/wireless/inc/authentication_attrs.html:5 +#: wireless/forms/bulk_edit.py:95 wireless/forms/bulk_edit.py:153 +#: wireless/forms/filtersets.py:39 wireless/forms/filtersets.py:104 +#: wireless/forms/model_forms.py:59 wireless/forms/model_forms.py:175 msgid "Authentication" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:430 netbox/ipam/models/vlans.py:63 +#: ipam/forms/bulk_edit.py:435 ipam/models/vlans.py:62 msgid "VLAN ID ranges" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:505 netbox/ipam/forms/bulk_import.py:501 -#: netbox/ipam/forms/filtersets.py:571 netbox/ipam/models/vlans.py:233 -#: netbox/ipam/tables/vlans.py:103 +#: ipam/forms/bulk_edit.py:516 ipam/forms/bulk_import.py:508 +#: ipam/forms/filtersets.py:579 ipam/models/vlans.py:239 +#: ipam/tables/vlans.py:105 msgid "Q-in-Q role" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:522 +#: ipam/forms/bulk_edit.py:533 msgid "Q-in-Q" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:523 +#: ipam/forms/bulk_edit.py:534 msgid "Site & Group" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:546 netbox/ipam/forms/bulk_import.py:531 -#: netbox/ipam/forms/model_forms.py:723 netbox/ipam/tables/vlans.py:256 -#: netbox/templates/ipam/vlantranslationrule.html:14 -#: netbox/vpn/forms/model_forms.py:322 netbox/vpn/forms/model_forms.py:359 +#: ipam/forms/bulk_edit.py:557 ipam/forms/bulk_import.py:538 +#: ipam/forms/model_forms.py:724 ipam/tables/vlans.py:258 +#: templates/ipam/vlantranslationrule.html:14 vpn/forms/model_forms.py:322 +#: vpn/forms/model_forms.py:359 msgid "Policy" msgstr "" -#: netbox/ipam/forms/bulk_edit.py:567 netbox/ipam/forms/model_forms.py:741 -#: netbox/ipam/forms/model_forms.py:773 netbox/ipam/tables/services.py:19 -#: netbox/ipam/tables/services.py:49 netbox/templates/ipam/service.html:36 -#: netbox/templates/ipam/servicetemplate.html:23 +#: ipam/forms/bulk_edit.py:578 ipam/forms/model_forms.py:742 +#: ipam/forms/model_forms.py:775 ipam/tables/services.py:19 +#: ipam/tables/services.py:49 templates/ipam/service.html:38 +#: templates/ipam/servicetemplate.html:23 msgid "Ports" msgstr "" -#: netbox/ipam/forms/bulk_import.py:51 +#: ipam/forms/bulk_import.py:51 msgid "Import route targets" msgstr "" -#: netbox/ipam/forms/bulk_import.py:57 +#: ipam/forms/bulk_import.py:57 msgid "Export route targets" msgstr "" -#: netbox/ipam/forms/bulk_import.py:95 netbox/ipam/forms/bulk_import.py:115 -#: netbox/ipam/forms/bulk_import.py:135 +#: ipam/forms/bulk_import.py:95 ipam/forms/bulk_import.py:115 +#: ipam/forms/bulk_import.py:135 msgid "Assigned RIR" msgstr "" -#: netbox/ipam/forms/bulk_import.py:178 +#: ipam/forms/bulk_import.py:178 msgid "VLAN's group (if any)" msgstr "" -#: netbox/ipam/forms/bulk_import.py:181 +#: ipam/forms/bulk_import.py:181 msgid "VLAN Site" msgstr "" -#: netbox/ipam/forms/bulk_import.py:185 +#: ipam/forms/bulk_import.py:185 msgid "VLAN's site (if any)" msgstr "" -#: netbox/ipam/forms/bulk_import.py:214 -#: netbox/virtualization/forms/bulk_import.py:80 -#: netbox/wireless/forms/bulk_import.py:83 +#: ipam/forms/bulk_import.py:214 virtualization/forms/bulk_import.py:80 +#: wireless/forms/bulk_import.py:83 msgid "Scope ID" msgstr "" -#: netbox/ipam/forms/bulk_import.py:331 netbox/ipam/forms/model_forms.py:305 -#: netbox/ipam/forms/model_forms.py:335 netbox/ipam/forms/model_forms.py:516 -#: netbox/templates/ipam/fhrpgroup.html:19 +#: ipam/forms/bulk_import.py:331 ipam/forms/filtersets.py:636 +#: ipam/forms/model_forms.py:305 ipam/forms/model_forms.py:335 +#: ipam/forms/model_forms.py:516 templates/ipam/fhrpgroup.html:19 msgid "FHRP Group" msgstr "" -#: netbox/ipam/forms/bulk_import.py:335 +#: ipam/forms/bulk_import.py:335 msgid "Assigned FHRP Group name" msgstr "" -#: netbox/ipam/forms/bulk_import.py:339 +#: ipam/forms/bulk_import.py:339 msgid "Make this the primary IP for the assigned device" msgstr "" -#: netbox/ipam/forms/bulk_import.py:343 +#: ipam/forms/bulk_import.py:343 msgid "Is out-of-band" msgstr "" -#: netbox/ipam/forms/bulk_import.py:344 +#: ipam/forms/bulk_import.py:344 msgid "Designate this as the out-of-band IP address for the assigned device" msgstr "" -#: netbox/ipam/forms/bulk_import.py:384 +#: ipam/forms/bulk_import.py:384 msgid "No device or virtual machine specified; cannot set as primary IP" msgstr "" -#: netbox/ipam/forms/bulk_import.py:388 +#: ipam/forms/bulk_import.py:388 msgid "No device specified; cannot set as out-of-band IP" msgstr "" -#: netbox/ipam/forms/bulk_import.py:392 +#: ipam/forms/bulk_import.py:392 msgid "Cannot set out-of-band IP for virtual machines" msgstr "" -#: netbox/ipam/forms/bulk_import.py:396 +#: ipam/forms/bulk_import.py:396 msgid "No interface specified; cannot set as primary IP" msgstr "" -#: netbox/ipam/forms/bulk_import.py:400 +#: ipam/forms/bulk_import.py:400 msgid "No interface specified; cannot set as out-of-band IP" msgstr "" -#: netbox/ipam/forms/bulk_import.py:437 +#: ipam/forms/bulk_import.py:437 msgid "Auth type" msgstr "" -#: netbox/ipam/forms/bulk_import.py:479 +#: ipam/forms/bulk_import.py:486 msgid "Assigned VLAN group" msgstr "" -#: netbox/ipam/forms/bulk_import.py:511 +#: ipam/forms/bulk_import.py:518 msgid "Service VLAN (for Q-in-Q/802.1ad customer VLANs)" msgstr "" -#: netbox/ipam/forms/bulk_import.py:534 netbox/ipam/models/vlans.py:352 +#: ipam/forms/bulk_import.py:541 ipam/models/vlans.py:358 msgid "VLAN translation policy" msgstr "" -#: netbox/ipam/forms/bulk_import.py:546 netbox/ipam/forms/bulk_import.py:572 +#: ipam/forms/bulk_import.py:553 ipam/forms/bulk_import.py:581 msgid "IP protocol" msgstr "" -#: netbox/ipam/forms/bulk_import.py:560 -msgid "Required if not assigned to a VM" +#: ipam/forms/bulk_import.py:565 +msgid "Parent type (app & model)" msgstr "" -#: netbox/ipam/forms/bulk_import.py:567 -msgid "Required if not assigned to a device" +#: ipam/forms/bulk_import.py:572 +msgid "Parent object name" msgstr "" -#: netbox/ipam/forms/bulk_import.py:592 +#: ipam/forms/bulk_import.py:576 +msgid "Parent object ID" +msgstr "" + +#: ipam/forms/bulk_import.py:628 +msgid "" +"One of parent or parent_object_id must be included with parent_object_type" +msgstr "" + +#: ipam/forms/bulk_import.py:638 #, python-brace-format -msgid "{ip} is not assigned to this device/VM." +msgid "{ip} is not assigned to this parent." msgstr "" -#: netbox/ipam/forms/filtersets.py:49 netbox/ipam/forms/model_forms.py:66 -#: netbox/netbox/navigation/menu.py:195 netbox/vpn/forms/model_forms.py:413 +#: ipam/forms/filtersets.py:49 ipam/forms/model_forms.py:66 +#: netbox/navigation/menu.py:196 vpn/forms/model_forms.py:413 msgid "Route Targets" msgstr "" -#: netbox/ipam/forms/filtersets.py:55 netbox/ipam/forms/model_forms.py:53 -#: netbox/vpn/forms/filtersets.py:230 netbox/vpn/forms/model_forms.py:400 +#: ipam/forms/filtersets.py:55 ipam/forms/model_forms.py:53 +#: vpn/forms/filtersets.py:235 vpn/forms/model_forms.py:400 msgid "Import targets" msgstr "" -#: netbox/ipam/forms/filtersets.py:60 netbox/ipam/forms/model_forms.py:58 -#: netbox/vpn/forms/filtersets.py:235 netbox/vpn/forms/model_forms.py:405 +#: ipam/forms/filtersets.py:60 ipam/forms/model_forms.py:58 +#: vpn/forms/filtersets.py:240 vpn/forms/model_forms.py:405 msgid "Export targets" msgstr "" -#: netbox/ipam/forms/filtersets.py:75 +#: ipam/forms/filtersets.py:75 msgid "Imported by VRF" msgstr "" -#: netbox/ipam/forms/filtersets.py:80 +#: ipam/forms/filtersets.py:80 msgid "Exported by VRF" msgstr "" -#: netbox/ipam/forms/filtersets.py:89 netbox/ipam/tables/ip.py:35 -#: netbox/templates/ipam/rir.html:30 +#: ipam/forms/filtersets.py:89 ipam/tables/ip.py:36 templates/ipam/rir.html:30 msgid "Private" msgstr "" -#: netbox/ipam/forms/filtersets.py:108 netbox/ipam/forms/filtersets.py:200 -#: netbox/ipam/forms/filtersets.py:288 netbox/ipam/forms/filtersets.py:343 +#: ipam/forms/filtersets.py:108 ipam/forms/filtersets.py:200 +#: ipam/forms/filtersets.py:288 ipam/forms/filtersets.py:350 msgid "Address family" msgstr "" -#: netbox/ipam/forms/filtersets.py:122 netbox/templates/ipam/asnrange.html:25 +#: ipam/forms/filtersets.py:122 templates/ipam/asnrange.html:25 msgid "Range" msgstr "" -#: netbox/ipam/forms/filtersets.py:131 +#: ipam/forms/filtersets.py:131 msgid "Start" msgstr "" -#: netbox/ipam/forms/filtersets.py:135 +#: ipam/forms/filtersets.py:135 msgid "End" msgstr "" -#: netbox/ipam/forms/filtersets.py:195 +#: ipam/forms/filtersets.py:195 msgid "Search within" msgstr "" -#: netbox/ipam/forms/filtersets.py:216 netbox/ipam/forms/filtersets.py:359 +#: ipam/forms/filtersets.py:216 ipam/forms/filtersets.py:366 msgid "Present in VRF" msgstr "" -#: netbox/ipam/forms/filtersets.py:327 +#: ipam/forms/filtersets.py:334 msgid "Device/VM" msgstr "" -#: netbox/ipam/forms/filtersets.py:338 +#: ipam/forms/filtersets.py:345 msgid "Parent Prefix" msgstr "" -#: netbox/ipam/forms/filtersets.py:383 +#: ipam/forms/filtersets.py:390 msgid "Assigned to an interface" msgstr "" -#: netbox/ipam/forms/filtersets.py:390 netbox/templates/ipam/ipaddress.html:51 +#: ipam/forms/filtersets.py:397 templates/ipam/ipaddress.html:51 msgid "DNS Name" msgstr "" -#: netbox/ipam/forms/filtersets.py:433 netbox/ipam/models/vlans.py:274 -#: netbox/ipam/tables/ip.py:122 netbox/ipam/tables/vlans.py:51 -#: netbox/ipam/views.py:1036 netbox/netbox/navigation/menu.py:199 -#: netbox/netbox/navigation/menu.py:201 +#: ipam/forms/filtersets.py:440 ipam/models/vlans.py:280 ipam/tables/ip.py:123 +#: ipam/tables/vlans.py:51 ipam/views.py:1015 netbox/navigation/menu.py:200 +#: netbox/navigation/menu.py:202 msgid "VLANs" msgstr "" -#: netbox/ipam/forms/filtersets.py:474 +#: ipam/forms/filtersets.py:482 msgid "Contains VLAN ID" msgstr "" -#: netbox/ipam/forms/filtersets.py:508 netbox/ipam/models/vlans.py:372 +#: ipam/forms/filtersets.py:516 ipam/models/vlans.py:378 msgid "Local VLAN ID" msgstr "" -#: netbox/ipam/forms/filtersets.py:513 netbox/ipam/models/vlans.py:380 +#: ipam/forms/filtersets.py:521 ipam/models/vlans.py:386 msgid "Remote VLAN ID" msgstr "" -#: netbox/ipam/forms/filtersets.py:523 +#: ipam/forms/filtersets.py:531 msgid "Q-in-Q/802.1ad" msgstr "" -#: netbox/ipam/forms/filtersets.py:568 netbox/ipam/models/vlans.py:192 -#: netbox/templates/ipam/vlan.html:31 +#: ipam/forms/filtersets.py:576 ipam/models/vlans.py:198 +#: templates/ipam/vlan.html:31 msgid "VLAN ID" msgstr "" -#: netbox/ipam/forms/model_forms.py:83 -#: netbox/templates/ipam/routetarget.html:10 +#: ipam/forms/model_forms.py:83 templates/ipam/routetarget.html:10 msgid "Route Target" msgstr "" -#: netbox/ipam/forms/model_forms.py:118 netbox/ipam/tables/ip.py:63 -#: netbox/templates/ipam/aggregate.html:11 netbox/templates/ipam/prefix.html:38 +#: ipam/forms/model_forms.py:118 ipam/tables/ip.py:64 +#: templates/ipam/aggregate.html:11 templates/ipam/prefix.html:38 msgid "Aggregate" msgstr "" -#: netbox/ipam/forms/model_forms.py:140 netbox/templates/ipam/asnrange.html:12 +#: ipam/forms/model_forms.py:140 templates/ipam/asnrange.html:12 msgid "ASN Range" msgstr "" -#: netbox/ipam/forms/model_forms.py:269 netbox/templates/ipam/iprange.html:10 +#: ipam/forms/model_forms.py:269 templates/ipam/iprange.html:10 msgid "IP Range" msgstr "" -#: netbox/ipam/forms/model_forms.py:320 +#: ipam/forms/model_forms.py:320 msgid "Make this the primary IP for the device/VM" msgstr "" -#: netbox/ipam/forms/model_forms.py:324 +#: ipam/forms/model_forms.py:324 msgid "Make this the out-of-band IP for the device" msgstr "" -#: netbox/ipam/forms/model_forms.py:339 +#: ipam/forms/model_forms.py:339 msgid "NAT IP (Inside)" msgstr "" -#: netbox/ipam/forms/model_forms.py:401 +#: ipam/forms/model_forms.py:401 msgid "An IP address can only be assigned to a single object." msgstr "" -#: netbox/ipam/forms/model_forms.py:408 +#: ipam/forms/model_forms.py:408 msgid "Cannot reassign primary IP address for the parent device/VM" msgstr "" -#: netbox/ipam/forms/model_forms.py:412 +#: ipam/forms/model_forms.py:412 msgid "Cannot reassign out-of-Band IP address for the parent device" msgstr "" -#: netbox/ipam/forms/model_forms.py:422 +#: ipam/forms/model_forms.py:422 msgid "" "Only IP addresses assigned to an interface can be designated as primary IPs." msgstr "" -#: netbox/ipam/forms/model_forms.py:430 +#: ipam/forms/model_forms.py:430 msgid "" "Only IP addresses assigned to a device interface can be designated as the " "out-of-band IP for a device." msgstr "" -#: netbox/ipam/forms/model_forms.py:518 +#: ipam/forms/model_forms.py:518 msgid "Virtual IP Address" msgstr "" -#: netbox/ipam/forms/model_forms.py:602 +#: ipam/forms/model_forms.py:602 msgid "Assignment already exists" msgstr "" -#: netbox/ipam/forms/model_forms.py:611 netbox/templates/ipam/vlangroup.html:42 +#: ipam/forms/model_forms.py:611 templates/ipam/vlangroup.html:42 msgid "VLAN IDs" msgstr "" -#: netbox/ipam/forms/model_forms.py:629 +#: ipam/forms/model_forms.py:629 msgid "Child VLANs" msgstr "" -#: netbox/ipam/forms/model_forms.py:729 -#: netbox/templates/ipam/vlantranslationrule.html:11 +#: ipam/forms/model_forms.py:730 templates/ipam/vlantranslationrule.html:11 msgid "VLAN Translation Rule" msgstr "" -#: netbox/ipam/forms/model_forms.py:746 netbox/ipam/forms/model_forms.py:778 +#: ipam/forms/model_forms.py:747 ipam/forms/model_forms.py:780 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." msgstr "" -#: netbox/ipam/forms/model_forms.py:751 -#: netbox/templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:752 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "" -#: netbox/ipam/forms/model_forms.py:798 +#: ipam/forms/model_forms.py:765 +msgid "Parent type" +msgstr "" + +#: ipam/forms/model_forms.py:796 msgid "Port(s)" msgstr "" -#: netbox/ipam/forms/model_forms.py:812 +#: ipam/forms/model_forms.py:839 msgid "Service template" msgstr "" -#: netbox/ipam/forms/model_forms.py:824 +#: ipam/forms/model_forms.py:848 msgid "From Template" msgstr "" -#: netbox/ipam/forms/model_forms.py:825 +#: ipam/forms/model_forms.py:849 msgid "Custom" msgstr "" -#: netbox/ipam/forms/model_forms.py:856 +#: ipam/forms/model_forms.py:880 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" -#: netbox/ipam/models/asns.py:34 +#: ipam/models/asns.py:34 msgid "start" msgstr "" -#: netbox/ipam/models/asns.py:51 +#: ipam/models/asns.py:51 msgid "ASN range" msgstr "" -#: netbox/ipam/models/asns.py:52 +#: ipam/models/asns.py:52 msgid "ASN ranges" msgstr "" -#: netbox/ipam/models/asns.py:69 +#: ipam/models/asns.py:69 #, python-brace-format msgid "Starting ASN ({start}) must be lower than ending ASN ({end})." msgstr "" -#: netbox/ipam/models/asns.py:101 +#: ipam/models/asns.py:101 msgid "Regional Internet Registry responsible for this AS number space" msgstr "" -#: netbox/ipam/models/asns.py:106 +#: ipam/models/asns.py:106 msgid "16- or 32-bit autonomous system number" msgstr "" -#: netbox/ipam/models/fhrp.py:21 +#: ipam/models/fhrp.py:21 msgid "group ID" msgstr "" -#: netbox/ipam/models/fhrp.py:29 netbox/ipam/models/services.py:21 +#: ipam/models/fhrp.py:29 ipam/models/services.py:21 msgid "protocol" msgstr "" -#: netbox/ipam/models/fhrp.py:38 netbox/wireless/models.py:29 +#: ipam/models/fhrp.py:38 wireless/models.py:29 msgid "authentication type" msgstr "" -#: netbox/ipam/models/fhrp.py:43 +#: ipam/models/fhrp.py:43 msgid "authentication key" msgstr "" -#: netbox/ipam/models/fhrp.py:56 +#: ipam/models/fhrp.py:62 msgid "FHRP group" msgstr "" -#: netbox/ipam/models/fhrp.py:57 +#: ipam/models/fhrp.py:63 msgid "FHRP groups" msgstr "" -#: netbox/ipam/models/fhrp.py:110 +#: ipam/models/fhrp.py:116 msgid "FHRP group assignment" msgstr "" -#: netbox/ipam/models/fhrp.py:111 +#: ipam/models/fhrp.py:117 msgid "FHRP group assignments" msgstr "" -#: netbox/ipam/models/ip.py:65 +#: ipam/models/ip.py:65 msgid "private" msgstr "" -#: netbox/ipam/models/ip.py:66 +#: ipam/models/ip.py:66 msgid "IP space managed by this RIR is considered private" msgstr "" -#: netbox/ipam/models/ip.py:72 netbox/netbox/navigation/menu.py:188 +#: ipam/models/ip.py:72 netbox/navigation/menu.py:189 msgid "RIRs" msgstr "" -#: netbox/ipam/models/ip.py:81 +#: ipam/models/ip.py:81 msgid "IPv4 or IPv6 network" msgstr "" -#: netbox/ipam/models/ip.py:88 +#: ipam/models/ip.py:88 msgid "Regional Internet Registry responsible for this IP space" msgstr "" -#: netbox/ipam/models/ip.py:98 +#: ipam/models/ip.py:98 msgid "date added" msgstr "" -#: netbox/ipam/models/ip.py:112 +#: ipam/models/ip.py:112 msgid "aggregate" msgstr "" -#: netbox/ipam/models/ip.py:113 +#: ipam/models/ip.py:113 msgid "aggregates" msgstr "" -#: netbox/ipam/models/ip.py:126 +#: ipam/models/ip.py:126 msgid "Cannot create aggregate with /0 mask." msgstr "" -#: netbox/ipam/models/ip.py:138 +#: ipam/models/ip.py:138 #, python-brace-format msgid "" "Aggregates cannot overlap. {prefix} is already covered by an existing " "aggregate ({aggregate})." msgstr "" -#: netbox/ipam/models/ip.py:152 +#: ipam/models/ip.py:152 #, python-brace-format msgid "" "Prefixes cannot overlap aggregates. {prefix} covers an existing aggregate " "({aggregate})." msgstr "" -#: netbox/ipam/models/ip.py:195 +#: ipam/models/ip.py:195 msgid "roles" msgstr "" -#: netbox/ipam/models/ip.py:208 netbox/ipam/models/ip.py:277 +#: ipam/models/ip.py:208 ipam/models/ip.py:277 msgid "prefix" msgstr "" -#: netbox/ipam/models/ip.py:209 +#: ipam/models/ip.py:209 msgid "IPv4 or IPv6 network with mask" msgstr "" -#: netbox/ipam/models/ip.py:238 +#: ipam/models/ip.py:238 msgid "Operational status of this prefix" msgstr "" -#: netbox/ipam/models/ip.py:246 +#: ipam/models/ip.py:246 msgid "The primary function of this prefix" msgstr "" -#: netbox/ipam/models/ip.py:249 +#: ipam/models/ip.py:249 msgid "is a pool" msgstr "" -#: netbox/ipam/models/ip.py:251 +#: ipam/models/ip.py:251 msgid "All IP addresses within this prefix are considered usable" msgstr "" -#: netbox/ipam/models/ip.py:254 netbox/ipam/models/ip.py:523 +#: ipam/models/ip.py:254 ipam/models/ip.py:531 msgid "mark utilized" msgstr "" -#: netbox/ipam/models/ip.py:278 +#: ipam/models/ip.py:278 msgid "prefixes" msgstr "" -#: netbox/ipam/models/ip.py:298 +#: ipam/models/ip.py:298 msgid "Cannot create prefix with /0 mask." msgstr "" -#: netbox/ipam/models/ip.py:305 netbox/ipam/models/ip.py:871 +#: ipam/models/ip.py:305 ipam/models/ip.py:881 #, python-brace-format msgid "VRF {vrf}" msgstr "" -#: netbox/ipam/models/ip.py:305 netbox/ipam/models/ip.py:871 +#: ipam/models/ip.py:305 ipam/models/ip.py:881 msgid "global table" msgstr "" -#: netbox/ipam/models/ip.py:307 +#: ipam/models/ip.py:307 #, python-brace-format msgid "Duplicate prefix found in {table}: {prefix}" msgstr "" -#: netbox/ipam/models/ip.py:481 +#: ipam/models/ip.py:484 msgid "start address" msgstr "" -#: netbox/ipam/models/ip.py:482 netbox/ipam/models/ip.py:486 -#: netbox/ipam/models/ip.py:711 +#: ipam/models/ip.py:485 ipam/models/ip.py:489 ipam/models/ip.py:722 msgid "IPv4 or IPv6 address (with mask)" msgstr "" -#: netbox/ipam/models/ip.py:485 +#: ipam/models/ip.py:488 msgid "end address" msgstr "" -#: netbox/ipam/models/ip.py:512 +#: ipam/models/ip.py:515 msgid "Operational status of this range" msgstr "" -#: netbox/ipam/models/ip.py:520 +#: ipam/models/ip.py:523 msgid "The primary function of this range" msgstr "" -#: netbox/ipam/models/ip.py:534 +#: ipam/models/ip.py:526 +msgid "mark populated" +msgstr "" + +#: ipam/models/ip.py:528 +msgid "Prevent the creation of IP addresses within this range" +msgstr "" + +#: ipam/models/ip.py:533 +#, python-format +msgid "Report space as 100% utilized" +msgstr "" + +#: ipam/models/ip.py:542 msgid "IP range" msgstr "" -#: netbox/ipam/models/ip.py:535 +#: ipam/models/ip.py:543 msgid "IP ranges" msgstr "" -#: netbox/ipam/models/ip.py:548 +#: ipam/models/ip.py:556 msgid "Starting and ending IP address versions must match" msgstr "" -#: netbox/ipam/models/ip.py:554 +#: ipam/models/ip.py:562 msgid "Starting and ending IP address masks must match" msgstr "" -#: netbox/ipam/models/ip.py:561 +#: ipam/models/ip.py:569 #, python-brace-format msgid "" "Ending address must be greater than the starting address ({start_address})" msgstr "" -#: netbox/ipam/models/ip.py:589 +#: ipam/models/ip.py:597 #, python-brace-format msgid "Defined addresses overlap with range {overlapping_range} in VRF {vrf}" msgstr "" -#: netbox/ipam/models/ip.py:598 +#: ipam/models/ip.py:606 #, python-brace-format msgid "Defined range exceeds maximum supported size ({max_size})" msgstr "" -#: netbox/ipam/models/ip.py:710 netbox/tenancy/models/contacts.py:77 +#: ipam/models/ip.py:721 tenancy/models/contacts.py:78 msgid "address" msgstr "" -#: netbox/ipam/models/ip.py:733 +#: ipam/models/ip.py:744 msgid "The operational status of this IP" msgstr "" -#: netbox/ipam/models/ip.py:741 +#: ipam/models/ip.py:752 msgid "The functional role of this IP" msgstr "" -#: netbox/ipam/models/ip.py:765 netbox/templates/ipam/ipaddress.html:72 +#: ipam/models/ip.py:775 templates/ipam/ipaddress.html:72 msgid "NAT (inside)" msgstr "" -#: netbox/ipam/models/ip.py:766 +#: ipam/models/ip.py:776 msgid "The IP for which this address is the \"outside\" IP" msgstr "" -#: netbox/ipam/models/ip.py:773 +#: ipam/models/ip.py:783 msgid "Hostname or FQDN (not case-sensitive)" msgstr "" -#: netbox/ipam/models/ip.py:789 netbox/ipam/models/services.py:90 +#: ipam/models/ip.py:799 ipam/models/services.py:86 msgid "IP addresses" msgstr "" -#: netbox/ipam/models/ip.py:842 +#: ipam/models/ip.py:852 msgid "Cannot create IP address with /0 mask." msgstr "" -#: netbox/ipam/models/ip.py:848 +#: ipam/models/ip.py:858 #, python-brace-format msgid "{ip} is a network ID, which may not be assigned to an interface." msgstr "" -#: netbox/ipam/models/ip.py:859 +#: ipam/models/ip.py:869 #, python-brace-format msgid "{ip} is a broadcast address, which may not be assigned to an interface." msgstr "" -#: netbox/ipam/models/ip.py:873 +#: ipam/models/ip.py:883 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "" -#: netbox/ipam/models/ip.py:896 +#: ipam/models/ip.py:899 +#, python-brace-format +msgid "Cannot create IP address {ip} inside range {range}." +msgstr "" + +#: ipam/models/ip.py:920 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" msgstr "" -#: netbox/ipam/models/ip.py:902 +#: ipam/models/ip.py:926 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "" -#: netbox/ipam/models/services.py:32 +#: ipam/models/services.py:32 msgid "port numbers" msgstr "" -#: netbox/ipam/models/services.py:58 +#: ipam/models/services.py:58 msgid "service template" msgstr "" -#: netbox/ipam/models/services.py:59 +#: ipam/models/services.py:59 msgid "service templates" msgstr "" -#: netbox/ipam/models/services.py:91 +#: ipam/models/services.py:87 msgid "The specific IP addresses (if any) to which this service is bound" msgstr "" -#: netbox/ipam/models/services.py:98 +#: ipam/models/services.py:97 msgid "service" msgstr "" -#: netbox/ipam/models/services.py:99 +#: ipam/models/services.py:98 msgid "services" msgstr "" -#: netbox/ipam/models/services.py:110 -msgid "" -"A service cannot be associated with both a device and a virtual machine." -msgstr "" - -#: netbox/ipam/models/services.py:112 -msgid "A service must be associated with either a device or a virtual machine." -msgstr "" - -#: netbox/ipam/models/vlans.py:88 +#: ipam/models/vlans.py:94 msgid "VLAN groups" msgstr "" -#: netbox/ipam/models/vlans.py:95 +#: ipam/models/vlans.py:101 msgid "Cannot set scope_type without scope_id." msgstr "" -#: netbox/ipam/models/vlans.py:97 +#: ipam/models/vlans.py:103 msgid "Cannot set scope_id without scope_type." msgstr "" -#: netbox/ipam/models/vlans.py:105 +#: ipam/models/vlans.py:111 #, python-brace-format msgid "Starting VLAN ID in range ({value}) cannot be less than {minimum}" msgstr "" -#: netbox/ipam/models/vlans.py:111 +#: ipam/models/vlans.py:117 #, python-brace-format msgid "Ending VLAN ID in range ({value}) cannot exceed {maximum}" msgstr "" -#: netbox/ipam/models/vlans.py:118 +#: ipam/models/vlans.py:124 #, python-brace-format msgid "" "Ending VLAN ID in range must be greater than or equal to the starting VLAN " "ID ({range})" msgstr "" -#: netbox/ipam/models/vlans.py:124 +#: ipam/models/vlans.py:130 msgid "Ranges cannot overlap." msgstr "" -#: netbox/ipam/models/vlans.py:181 +#: ipam/models/vlans.py:187 msgid "The specific site to which this VLAN is assigned (if any)" msgstr "" -#: netbox/ipam/models/vlans.py:189 +#: ipam/models/vlans.py:195 msgid "VLAN group (optional)" msgstr "" -#: netbox/ipam/models/vlans.py:197 netbox/ipam/models/vlans.py:377 -#: netbox/ipam/models/vlans.py:385 +#: ipam/models/vlans.py:203 ipam/models/vlans.py:383 ipam/models/vlans.py:391 msgid "Numeric VLAN ID (1-4094)" msgstr "" -#: netbox/ipam/models/vlans.py:215 +#: ipam/models/vlans.py:221 msgid "Operational status of this VLAN" msgstr "" -#: netbox/ipam/models/vlans.py:223 +#: ipam/models/vlans.py:229 msgid "The primary function of this VLAN" msgstr "" -#: netbox/ipam/models/vlans.py:238 +#: ipam/models/vlans.py:244 msgid "Customer/service VLAN designation (for Q-in-Q/IEEE 802.1ad)" msgstr "" -#: netbox/ipam/models/vlans.py:287 +#: ipam/models/vlans.py:293 #, python-brace-format msgid "" "VLAN is assigned to group {group} (scope: {scope}); cannot also assign to " "site {site}." msgstr "" -#: netbox/ipam/models/vlans.py:294 +#: ipam/models/vlans.py:300 #, python-brace-format msgid "" "The assigned site {site} is not a member of the assigned group {group} " "(scope: {scope})." msgstr "" -#: netbox/ipam/models/vlans.py:303 +#: ipam/models/vlans.py:309 #, python-brace-format msgid "VID must be in ranges {ranges} for VLANs in group {group}" msgstr "" -#: netbox/ipam/models/vlans.py:310 +#: ipam/models/vlans.py:316 msgid "Only Q-in-Q customer VLANs maybe assigned to a service VLAN." msgstr "" -#: netbox/ipam/models/vlans.py:316 +#: ipam/models/vlans.py:322 msgid "A Q-in-Q customer VLAN must be assigned to a service VLAN." msgstr "" -#: netbox/ipam/models/vlans.py:353 +#: ipam/models/vlans.py:359 msgid "VLAN translation policies" msgstr "" -#: netbox/ipam/models/vlans.py:394 +#: ipam/models/vlans.py:400 msgid "VLAN translation rule" msgstr "" -#: netbox/ipam/models/vrfs.py:29 +#: ipam/models/vrfs.py:29 msgid "route distinguisher" msgstr "" -#: netbox/ipam/models/vrfs.py:30 +#: ipam/models/vrfs.py:30 msgid "Unique route distinguisher (as defined in RFC 4364)" msgstr "" -#: netbox/ipam/models/vrfs.py:41 +#: ipam/models/vrfs.py:41 msgid "enforce unique space" msgstr "" -#: netbox/ipam/models/vrfs.py:42 +#: ipam/models/vrfs.py:42 msgid "Prevent duplicate prefixes/IP addresses within this VRF" msgstr "" -#: netbox/ipam/models/vrfs.py:62 netbox/netbox/navigation/menu.py:192 -#: netbox/netbox/navigation/menu.py:194 +#: ipam/models/vrfs.py:62 netbox/navigation/menu.py:193 +#: netbox/navigation/menu.py:195 msgid "VRFs" msgstr "" -#: netbox/ipam/models/vrfs.py:78 +#: ipam/models/vrfs.py:78 msgid "Route target value (formatted in accordance with RFC 4360)" msgstr "" -#: netbox/ipam/models/vrfs.py:91 +#: ipam/models/vrfs.py:91 msgid "route target" msgstr "" -#: netbox/ipam/models/vrfs.py:92 +#: ipam/models/vrfs.py:92 msgid "route targets" msgstr "" -#: netbox/ipam/tables/asn.py:52 +#: ipam/tables/asn.py:52 msgid "ASDOT" msgstr "" -#: netbox/ipam/tables/asn.py:57 +#: ipam/tables/asn.py:57 msgid "Site Count" msgstr "" -#: netbox/ipam/tables/asn.py:62 +#: ipam/tables/asn.py:62 msgid "Provider Count" msgstr "" -#: netbox/ipam/tables/ip.py:41 netbox/netbox/navigation/menu.py:185 -#: netbox/netbox/navigation/menu.py:187 +#: ipam/tables/ip.py:42 netbox/navigation/menu.py:186 +#: netbox/navigation/menu.py:188 msgid "Aggregates" msgstr "" -#: netbox/ipam/tables/ip.py:71 +#: ipam/tables/ip.py:72 msgid "Added" msgstr "" -#: netbox/ipam/tables/ip.py:74 netbox/ipam/tables/ip.py:112 -#: netbox/ipam/tables/vlans.py:118 netbox/ipam/views.py:373 -#: netbox/netbox/navigation/menu.py:171 netbox/netbox/navigation/menu.py:173 -#: netbox/templates/ipam/vlan.html:100 +#: ipam/tables/ip.py:75 ipam/tables/ip.py:113 ipam/tables/vlans.py:120 +#: ipam/views.py:372 netbox/navigation/menu.py:172 +#: netbox/navigation/menu.py:174 templates/ipam/vlan.html:100 msgid "Prefixes" msgstr "" -#: netbox/ipam/tables/ip.py:77 netbox/ipam/tables/ip.py:221 -#: netbox/ipam/tables/ip.py:276 netbox/ipam/tables/vlans.py:55 -#: netbox/templates/dcim/device.html:260 -#: netbox/templates/ipam/aggregate.html:24 -#: netbox/templates/ipam/iprange.html:29 netbox/templates/ipam/prefix.html:102 +#: ipam/tables/ip.py:78 ipam/tables/ip.py:222 ipam/tables/ip.py:281 +#: ipam/tables/vlans.py:55 templates/dcim/device.html:260 +#: templates/ipam/aggregate.html:24 templates/ipam/iprange.html:37 +#: templates/ipam/prefix.html:102 msgid "Utilization" msgstr "" -#: netbox/ipam/tables/ip.py:117 netbox/netbox/navigation/menu.py:167 +#: ipam/tables/ip.py:118 netbox/navigation/menu.py:168 msgid "IP Ranges" msgstr "" -#: netbox/ipam/tables/ip.py:167 +#: ipam/tables/ip.py:168 msgid "Prefix (Flat)" msgstr "" -#: netbox/ipam/tables/ip.py:171 +#: ipam/tables/ip.py:172 msgid "Depth" msgstr "" -#: netbox/ipam/tables/ip.py:191 netbox/ipam/tables/vlans.py:37 -#: netbox/virtualization/tables/clusters.py:77 -#: netbox/wireless/tables/wirelesslan.py:55 +#: ipam/tables/ip.py:192 ipam/tables/vlans.py:37 +#: virtualization/tables/clusters.py:77 wireless/tables/wirelesslan.py:55 msgid "Scope Type" msgstr "" -#: netbox/ipam/tables/ip.py:213 +#: ipam/tables/ip.py:214 msgid "Pool" msgstr "" -#: netbox/ipam/tables/ip.py:217 netbox/ipam/tables/ip.py:272 +#: ipam/tables/ip.py:218 ipam/tables/ip.py:277 templates/ipam/iprange.html:33 msgid "Marked Utilized" msgstr "" -#: netbox/ipam/tables/ip.py:256 +#: ipam/tables/ip.py:257 msgid "Start address" msgstr "" -#: netbox/ipam/tables/ip.py:335 +#: ipam/tables/ip.py:273 templates/ipam/iprange.html:29 +msgid "Marked Populated" +msgstr "" + +#: ipam/tables/ip.py:341 msgid "NAT (Inside)" msgstr "" -#: netbox/ipam/tables/ip.py:340 +#: ipam/tables/ip.py:346 msgid "NAT (Outside)" msgstr "" -#: netbox/ipam/tables/ip.py:345 +#: ipam/tables/ip.py:351 msgid "Assigned" msgstr "" -#: netbox/ipam/tables/ip.py:381 netbox/templates/vpn/l2vpntermination.html:16 -#: netbox/vpn/forms/filtersets.py:246 +#: ipam/tables/ip.py:397 templates/vpn/l2vpntermination.html:16 +#: vpn/forms/filtersets.py:251 msgid "Assigned Object" msgstr "" -#: netbox/ipam/tables/vlans.py:45 +#: ipam/tables/vlans.py:45 msgid "VID Ranges" msgstr "" -#: netbox/ipam/tables/vlans.py:80 netbox/ipam/tables/vlans.py:190 -#: netbox/templates/dcim/inc/interface_vlans_table.html:4 +#: ipam/tables/vlans.py:82 ipam/tables/vlans.py:192 +#: templates/dcim/inc/interface_vlans_table.html:4 msgid "VID" msgstr "" -#: netbox/ipam/tables/vlans.py:237 -#: netbox/templates/ipam/vlantranslationpolicy.html:22 +#: ipam/tables/vlans.py:239 templates/ipam/vlantranslationpolicy.html:22 msgid "Rules" msgstr "" -#: netbox/ipam/tables/vlans.py:260 -#: netbox/templates/ipam/vlantranslationrule.html:18 +#: ipam/tables/vlans.py:262 templates/ipam/vlantranslationrule.html:18 msgid "Local VID" msgstr "" -#: netbox/ipam/tables/vlans.py:264 -#: netbox/templates/ipam/vlantranslationrule.html:22 +#: ipam/tables/vlans.py:266 templates/ipam/vlantranslationrule.html:22 msgid "Remote VID" msgstr "" -#: netbox/ipam/tables/vrfs.py:30 +#: ipam/tables/vrfs.py:30 msgid "RD" msgstr "" -#: netbox/ipam/tables/vrfs.py:33 +#: ipam/tables/vrfs.py:33 msgid "Unique" msgstr "" -#: netbox/ipam/tables/vrfs.py:37 netbox/vpn/tables/l2vpn.py:27 +#: ipam/tables/vrfs.py:37 vpn/tables/l2vpn.py:30 msgid "Import Targets" msgstr "" -#: netbox/ipam/tables/vrfs.py:42 netbox/vpn/tables/l2vpn.py:32 +#: ipam/tables/vrfs.py:42 vpn/tables/l2vpn.py:35 msgid "Export Targets" msgstr "" -#: netbox/ipam/validators.py:9 +#: ipam/utils.py:30 +msgid "1 IP available" +msgstr "" + +#: ipam/utils.py:32 +#, python-brace-format +msgid "{count} IPs available" +msgstr "" + +#: ipam/utils.py:33 +msgid "Many IPs available" +msgstr "" + +#: ipam/validators.py:9 #, python-brace-format msgid "{prefix} is not a valid prefix. Did you mean {suggested}?" msgstr "" -#: netbox/ipam/validators.py:16 +#: ipam/validators.py:16 #, python-format msgid "The prefix length must be less than or equal to %(limit_value)s." msgstr "" -#: netbox/ipam/validators.py:24 +#: ipam/validators.py:24 #, python-format msgid "The prefix length must be greater than or equal to %(limit_value)s." msgstr "" -#: netbox/ipam/validators.py:33 +#: ipam/validators.py:33 msgid "" "Only alphanumeric characters, asterisks, hyphens, periods, and underscores " "are allowed in DNS names" msgstr "" -#: netbox/ipam/views.py:570 +#: ipam/views.py:564 msgid "Child Prefixes" msgstr "" -#: netbox/ipam/views.py:606 +#: ipam/views.py:600 msgid "Child Ranges" msgstr "" -#: netbox/ipam/views.py:958 +#: ipam/views.py:942 msgid "Related IPs" msgstr "" -#: netbox/ipam/views.py:1315 +#: ipam/views.py:1306 msgid "Device Interfaces" msgstr "" -#: netbox/ipam/views.py:1333 +#: ipam/views.py:1324 msgid "VM Interfaces" msgstr "" -#: netbox/netbox/api/fields.py:65 +#: netbox/api/fields.py:66 msgid "This field may not be blank." msgstr "" -#: netbox/netbox/api/fields.py:70 +#: netbox/api/fields.py:71 msgid "" "Value must be passed directly (e.g. \"foo\": 123); do not use a dictionary " "or list." msgstr "" -#: netbox/netbox/api/fields.py:91 +#: netbox/api/fields.py:92 #, python-brace-format msgid "{value} is not a valid choice." msgstr "" -#: netbox/netbox/api/fields.py:104 +#: netbox/api/fields.py:105 #, python-brace-format msgid "Invalid content type: {content_type}" msgstr "" -#: netbox/netbox/api/fields.py:105 +#: netbox/api/fields.py:106 msgid "Invalid value. Specify a content type as '.'." msgstr "" -#: netbox/netbox/api/fields.py:167 +#: netbox/api/fields.py:168 msgid "Ranges must be specified in the form (lower, upper)." msgstr "" -#: netbox/netbox/api/fields.py:169 +#: netbox/api/fields.py:170 msgid "Range boundaries must be defined as integers." msgstr "" -#: netbox/netbox/api/serializers/fields.py:40 +#: netbox/api/serializers/fields.py:40 #, python-brace-format msgid "{class_name} must implement get_view_name()" msgstr "" -#: netbox/netbox/authentication/__init__.py:138 +#: netbox/authentication/__init__.py:138 #, python-brace-format msgid "Invalid permission {permission} for model {model}" msgstr "" -#: netbox/netbox/choices.py:51 +#: netbox/choices.py:51 msgid "Dark Red" msgstr "" -#: netbox/netbox/choices.py:54 +#: netbox/choices.py:54 msgid "Rose" msgstr "" -#: netbox/netbox/choices.py:55 +#: netbox/choices.py:55 msgid "Fuchsia" msgstr "" -#: netbox/netbox/choices.py:57 +#: netbox/choices.py:57 msgid "Dark Purple" msgstr "" -#: netbox/netbox/choices.py:60 +#: netbox/choices.py:60 msgid "Light Blue" msgstr "" -#: netbox/netbox/choices.py:63 +#: netbox/choices.py:63 msgid "Aqua" msgstr "" -#: netbox/netbox/choices.py:64 +#: netbox/choices.py:64 msgid "Dark Green" msgstr "" -#: netbox/netbox/choices.py:66 +#: netbox/choices.py:66 msgid "Light Green" msgstr "" -#: netbox/netbox/choices.py:67 +#: netbox/choices.py:67 msgid "Lime" msgstr "" -#: netbox/netbox/choices.py:69 +#: netbox/choices.py:69 msgid "Amber" msgstr "" -#: netbox/netbox/choices.py:71 +#: netbox/choices.py:71 msgid "Dark Orange" msgstr "" -#: netbox/netbox/choices.py:72 +#: netbox/choices.py:72 msgid "Brown" msgstr "" -#: netbox/netbox/choices.py:73 +#: netbox/choices.py:73 msgid "Light Grey" msgstr "" -#: netbox/netbox/choices.py:74 +#: netbox/choices.py:74 msgid "Grey" msgstr "" -#: netbox/netbox/choices.py:75 +#: netbox/choices.py:75 msgid "Dark Grey" msgstr "" -#: netbox/netbox/choices.py:103 netbox/templates/extras/script_result.html:56 +#: netbox/choices.py:103 templates/extras/script_result.html:56 +#: templates/extras/tableconfig.html:76 msgid "Default" msgstr "" -#: netbox/netbox/choices.py:130 +#: netbox/choices.py:130 msgid "Direct" msgstr "" -#: netbox/netbox/choices.py:131 +#: netbox/choices.py:131 msgid "Upload" msgstr "" -#: netbox/netbox/choices.py:143 netbox/netbox/choices.py:157 +#: netbox/choices.py:143 netbox/choices.py:157 msgid "Auto-detect" msgstr "" -#: netbox/netbox/choices.py:158 +#: netbox/choices.py:158 msgid "Comma" msgstr "" -#: netbox/netbox/choices.py:159 +#: netbox/choices.py:159 msgid "Semicolon" msgstr "" -#: netbox/netbox/choices.py:160 +#: netbox/choices.py:160 msgid "Tab" msgstr "" -#: netbox/netbox/choices.py:193 netbox/templates/dcim/device.html:327 -#: netbox/templates/dcim/rack.html:107 +#: netbox/choices.py:193 templates/dcim/device.html:327 +#: templates/dcim/rack.html:107 msgid "Kilograms" msgstr "" -#: netbox/netbox/choices.py:194 +#: netbox/choices.py:194 msgid "Grams" msgstr "" -#: netbox/netbox/choices.py:195 netbox/templates/dcim/device.html:328 -#: netbox/templates/dcim/rack.html:108 +#: netbox/choices.py:195 templates/dcim/device.html:328 +#: templates/dcim/rack.html:108 msgid "Pounds" msgstr "" -#: netbox/netbox/choices.py:196 +#: netbox/choices.py:196 msgid "Ounces" msgstr "" -#: netbox/netbox/config/__init__.py:67 +#: netbox/config/__init__.py:67 #, python-brace-format msgid "Invalid configuration parameter: {item}" msgstr "" -#: netbox/netbox/config/parameters.py:22 -#: netbox/templates/core/inc/config_data.html:62 +#: netbox/config/parameters.py:22 templates/core/inc/config_data.html:62 msgid "Login banner" msgstr "" -#: netbox/netbox/config/parameters.py:24 +#: netbox/config/parameters.py:24 msgid "Additional content to display on the login page" msgstr "" -#: netbox/netbox/config/parameters.py:33 -#: netbox/templates/core/inc/config_data.html:66 +#: netbox/config/parameters.py:33 templates/core/inc/config_data.html:66 msgid "Maintenance banner" msgstr "" -#: netbox/netbox/config/parameters.py:35 +#: netbox/config/parameters.py:35 msgid "Additional content to display when in maintenance mode" msgstr "" -#: netbox/netbox/config/parameters.py:44 -#: netbox/templates/core/inc/config_data.html:70 +#: netbox/config/parameters.py:44 templates/core/inc/config_data.html:70 msgid "Top banner" msgstr "" -#: netbox/netbox/config/parameters.py:46 +#: netbox/config/parameters.py:46 msgid "Additional content to display at the top of every page" msgstr "" -#: netbox/netbox/config/parameters.py:55 -#: netbox/templates/core/inc/config_data.html:74 +#: netbox/config/parameters.py:55 templates/core/inc/config_data.html:74 msgid "Bottom banner" msgstr "" -#: netbox/netbox/config/parameters.py:57 +#: netbox/config/parameters.py:57 msgid "Additional content to display at the bottom of every page" msgstr "" -#: netbox/netbox/config/parameters.py:68 +#: netbox/config/parameters.py:68 msgid "Globally unique IP space" msgstr "" -#: netbox/netbox/config/parameters.py:70 +#: netbox/config/parameters.py:70 msgid "Enforce unique IP addressing within the global table" msgstr "" -#: netbox/netbox/config/parameters.py:75 -#: netbox/templates/core/inc/config_data.html:44 +#: netbox/config/parameters.py:75 templates/core/inc/config_data.html:44 msgid "Prefer IPv4" msgstr "" -#: netbox/netbox/config/parameters.py:77 +#: netbox/config/parameters.py:77 msgid "Prefer IPv4 addresses over IPv6" msgstr "" -#: netbox/netbox/config/parameters.py:84 +#: netbox/config/parameters.py:84 msgid "Rack unit height" msgstr "" -#: netbox/netbox/config/parameters.py:86 +#: netbox/config/parameters.py:86 msgid "Default unit height for rendered rack elevations" msgstr "" -#: netbox/netbox/config/parameters.py:91 +#: netbox/config/parameters.py:91 msgid "Rack unit width" msgstr "" -#: netbox/netbox/config/parameters.py:93 +#: netbox/config/parameters.py:93 msgid "Default unit width for rendered rack elevations" msgstr "" -#: netbox/netbox/config/parameters.py:100 +#: netbox/config/parameters.py:100 msgid "Powerfeed voltage" msgstr "" -#: netbox/netbox/config/parameters.py:102 +#: netbox/config/parameters.py:102 msgid "Default voltage for powerfeeds" msgstr "" -#: netbox/netbox/config/parameters.py:107 +#: netbox/config/parameters.py:107 msgid "Powerfeed amperage" msgstr "" -#: netbox/netbox/config/parameters.py:109 +#: netbox/config/parameters.py:109 msgid "Default amperage for powerfeeds" msgstr "" -#: netbox/netbox/config/parameters.py:114 +#: netbox/config/parameters.py:114 msgid "Powerfeed max utilization" msgstr "" -#: netbox/netbox/config/parameters.py:116 +#: netbox/config/parameters.py:116 msgid "Default max utilization for powerfeeds" msgstr "" -#: netbox/netbox/config/parameters.py:123 -#: netbox/templates/core/inc/config_data.html:53 +#: netbox/config/parameters.py:123 templates/core/inc/config_data.html:53 msgid "Allowed URL schemes" msgstr "" -#: netbox/netbox/config/parameters.py:128 +#: netbox/config/parameters.py:128 msgid "Permitted schemes for URLs in user-provided content" msgstr "" -#: netbox/netbox/config/parameters.py:136 +#: netbox/config/parameters.py:136 msgid "Default page size" msgstr "" -#: netbox/netbox/config/parameters.py:142 +#: netbox/config/parameters.py:142 msgid "Maximum page size" msgstr "" -#: netbox/netbox/config/parameters.py:150 -#: netbox/templates/core/inc/config_data.html:96 +#: netbox/config/parameters.py:150 templates/core/inc/config_data.html:96 msgid "Custom validators" msgstr "" -#: netbox/netbox/config/parameters.py:152 +#: netbox/config/parameters.py:152 msgid "Custom validation rules (JSON)" msgstr "" -#: netbox/netbox/config/parameters.py:160 -#: netbox/templates/core/inc/config_data.html:104 +#: netbox/config/parameters.py:160 templates/core/inc/config_data.html:104 msgid "Protection rules" msgstr "" -#: netbox/netbox/config/parameters.py:162 +#: netbox/config/parameters.py:162 msgid "Deletion protection rules (JSON)" msgstr "" -#: netbox/netbox/config/parameters.py:172 -#: netbox/templates/core/inc/config_data.html:117 +#: netbox/config/parameters.py:172 templates/core/inc/config_data.html:117 msgid "Default preferences" msgstr "" -#: netbox/netbox/config/parameters.py:174 +#: netbox/config/parameters.py:174 msgid "Default preferences for new users" msgstr "" -#: netbox/netbox/config/parameters.py:181 -#: netbox/templates/core/inc/config_data.html:129 +#: netbox/config/parameters.py:181 templates/core/inc/config_data.html:129 msgid "Maintenance mode" msgstr "" -#: netbox/netbox/config/parameters.py:183 +#: netbox/config/parameters.py:183 msgid "Enable maintenance mode" msgstr "" -#: netbox/netbox/config/parameters.py:188 -#: netbox/templates/core/inc/config_data.html:133 +#: netbox/config/parameters.py:188 templates/core/inc/config_data.html:133 msgid "GraphQL enabled" msgstr "" -#: netbox/netbox/config/parameters.py:190 +#: netbox/config/parameters.py:190 msgid "Enable the GraphQL API" msgstr "" -#: netbox/netbox/config/parameters.py:195 -#: netbox/templates/core/inc/config_data.html:137 +#: netbox/config/parameters.py:195 templates/core/inc/config_data.html:137 msgid "Changelog retention" msgstr "" -#: netbox/netbox/config/parameters.py:197 +#: netbox/config/parameters.py:197 msgid "Days to retain changelog history (set to zero for unlimited)" msgstr "" -#: netbox/netbox/config/parameters.py:202 +#: netbox/config/parameters.py:202 msgid "Job result retention" msgstr "" -#: netbox/netbox/config/parameters.py:204 +#: netbox/config/parameters.py:204 msgid "Days to retain job result history (set to zero for unlimited)" msgstr "" -#: netbox/netbox/config/parameters.py:209 -#: netbox/templates/core/inc/config_data.html:145 +#: netbox/config/parameters.py:209 templates/core/inc/config_data.html:145 msgid "Maps URL" msgstr "" -#: netbox/netbox/config/parameters.py:211 +#: netbox/config/parameters.py:211 msgid "Base URL for mapping geographic locations" msgstr "" -#: netbox/netbox/forms/__init__.py:12 +#: netbox/forms/__init__.py:12 msgid "Partial match" msgstr "" -#: netbox/netbox/forms/__init__.py:13 +#: netbox/forms/__init__.py:13 msgid "Exact match" msgstr "" -#: netbox/netbox/forms/__init__.py:14 +#: netbox/forms/__init__.py:14 msgid "Starts with" msgstr "" -#: netbox/netbox/forms/__init__.py:15 +#: netbox/forms/__init__.py:15 msgid "Ends with" msgstr "" -#: netbox/netbox/forms/__init__.py:16 +#: netbox/forms/__init__.py:16 msgid "Regex" msgstr "" -#: netbox/netbox/forms/__init__.py:34 +#: netbox/forms/__init__.py:34 msgid "Object type(s)" msgstr "" -#: netbox/netbox/forms/__init__.py:40 +#: netbox/forms/__init__.py:40 msgid "Lookup" msgstr "" -#: netbox/netbox/forms/base.py:90 +#: netbox/forms/base.py:90 msgid "" "Tag slugs separated by commas, encased with double quotes (e.g. \"tag1,tag2," "tag3\")" msgstr "" -#: netbox/netbox/forms/base.py:120 +#: netbox/forms/base.py:120 msgid "Add tags" msgstr "" -#: netbox/netbox/forms/base.py:125 +#: netbox/forms/base.py:125 msgid "Remove tags" msgstr "" -#: netbox/netbox/forms/mixins.py:38 +#: netbox/forms/mixins.py:38 #, python-brace-format msgid "{class_name} must specify a model class." msgstr "" -#: netbox/netbox/models/features.py:280 +#: netbox/models/features.py:280 #, python-brace-format msgid "Unknown field name '{name}' in custom field data." msgstr "" -#: netbox/netbox/models/features.py:286 +#: netbox/models/features.py:286 #, python-brace-format msgid "Invalid value for custom field '{name}': {error}" msgstr "" -#: netbox/netbox/models/features.py:295 +#: netbox/models/features.py:295 #, python-brace-format msgid "Custom field '{name}' must have a unique value." msgstr "" -#: netbox/netbox/models/features.py:302 +#: netbox/models/features.py:302 #, python-brace-format msgid "Missing required custom field '{name}'." msgstr "" -#: netbox/netbox/models/features.py:470 +#: netbox/models/features.py:492 msgid "Remote data source" msgstr "" -#: netbox/netbox/models/features.py:480 +#: netbox/models/features.py:502 msgid "data path" msgstr "" -#: netbox/netbox/models/features.py:484 +#: netbox/models/features.py:506 msgid "Path to remote file (relative to data source root)" msgstr "" -#: netbox/netbox/models/features.py:487 +#: netbox/models/features.py:509 msgid "auto sync enabled" msgstr "" -#: netbox/netbox/models/features.py:489 +#: netbox/models/features.py:511 msgid "Enable automatic synchronization of data when the data file is updated" msgstr "" -#: netbox/netbox/models/features.py:492 +#: netbox/models/features.py:514 msgid "date synced" msgstr "" -#: netbox/netbox/models/features.py:586 +#: netbox/models/features.py:608 #, python-brace-format msgid "{class_name} must implement a sync_data() method." msgstr "" -#: netbox/netbox/models/mixins.py:22 +#: netbox/models/mixins.py:22 msgid "weight unit" msgstr "" -#: netbox/netbox/models/mixins.py:52 +#: netbox/models/mixins.py:52 msgid "Must specify a unit when setting a weight" msgstr "" -#: netbox/netbox/models/mixins.py:57 +#: netbox/models/mixins.py:57 msgid "distance" msgstr "" -#: netbox/netbox/models/mixins.py:64 +#: netbox/models/mixins.py:64 msgid "distance unit" msgstr "" -#: netbox/netbox/models/mixins.py:99 +#: netbox/models/mixins.py:99 msgid "Must specify a unit when setting a distance" msgstr "" -#: netbox/netbox/navigation/menu.py:11 +#: netbox/navigation/menu.py:11 msgid "Organization" msgstr "" -#: netbox/netbox/navigation/menu.py:19 +#: netbox/navigation/menu.py:18 msgid "Site Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:27 +#: netbox/navigation/menu.py:27 msgid "Tenant Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:34 +#: netbox/navigation/menu.py:34 msgid "Contact Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:35 -#: netbox/templates/tenancy/contactrole.html:8 +#: netbox/navigation/menu.py:35 templates/tenancy/contactrole.html:8 msgid "Contact Roles" msgstr "" -#: netbox/netbox/navigation/menu.py:36 +#: netbox/navigation/menu.py:36 msgid "Contact Assignments" msgstr "" -#: netbox/netbox/navigation/menu.py:50 +#: netbox/navigation/menu.py:50 msgid "Rack Roles" msgstr "" -#: netbox/netbox/navigation/menu.py:54 +#: netbox/navigation/menu.py:54 msgid "Elevations" msgstr "" -#: netbox/netbox/navigation/menu.py:76 +#: netbox/navigation/menu.py:76 msgid "Modules" msgstr "" -#: netbox/netbox/navigation/menu.py:80 netbox/templates/dcim/device.html:160 -#: netbox/templates/dcim/virtualdevicecontext.html:8 +#: netbox/navigation/menu.py:80 templates/dcim/device.html:160 +#: templates/dcim/virtualdevicecontext.html:8 msgid "Virtual Device Contexts" msgstr "" -#: netbox/netbox/navigation/menu.py:88 +#: netbox/navigation/menu.py:88 +msgid "Module Type Profiles" +msgstr "" + +#: netbox/navigation/menu.py:89 msgid "Manufacturers" msgstr "" -#: netbox/netbox/navigation/menu.py:92 +#: netbox/navigation/menu.py:93 msgid "Device Components" msgstr "" -#: netbox/netbox/navigation/menu.py:104 -#: netbox/templates/dcim/inventoryitemrole.html:8 +#: netbox/navigation/menu.py:105 templates/dcim/inventoryitemrole.html:8 msgid "Inventory Item Roles" msgstr "" -#: netbox/netbox/navigation/menu.py:110 -#: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:124 +#: netbox/navigation/menu.py:111 templates/dcim/interface.html:413 +#: templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "" -#: netbox/netbox/navigation/menu.py:117 netbox/netbox/navigation/menu.py:121 -#: netbox/templates/dcim/interface.html:182 +#: netbox/navigation/menu.py:118 netbox/navigation/menu.py:122 +#: templates/dcim/interface.html:182 msgid "Connections" msgstr "" -#: netbox/netbox/navigation/menu.py:123 +#: netbox/navigation/menu.py:124 msgid "Cables" msgstr "" -#: netbox/netbox/navigation/menu.py:124 +#: netbox/navigation/menu.py:125 msgid "Wireless Links" msgstr "" -#: netbox/netbox/navigation/menu.py:127 +#: netbox/navigation/menu.py:128 msgid "Interface Connections" msgstr "" -#: netbox/netbox/navigation/menu.py:132 +#: netbox/navigation/menu.py:133 msgid "Console Connections" msgstr "" -#: netbox/netbox/navigation/menu.py:137 +#: netbox/navigation/menu.py:138 msgid "Power Connections" msgstr "" -#: netbox/netbox/navigation/menu.py:153 +#: netbox/navigation/menu.py:154 msgid "Wireless LAN Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:174 +#: netbox/navigation/menu.py:175 msgid "Prefix & VLAN Roles" msgstr "" -#: netbox/netbox/navigation/menu.py:180 +#: netbox/navigation/menu.py:181 msgid "ASN Ranges" msgstr "" -#: netbox/netbox/navigation/menu.py:203 +#: netbox/navigation/menu.py:204 msgid "VLAN Translation Policies" msgstr "" -#: netbox/netbox/navigation/menu.py:204 -#: netbox/templates/ipam/vlantranslationpolicy.html:46 +#: netbox/navigation/menu.py:205 templates/ipam/vlantranslationpolicy.html:46 msgid "VLAN Translation Rules" msgstr "" -#: netbox/netbox/navigation/menu.py:211 +#: netbox/navigation/menu.py:212 msgid "Service Templates" msgstr "" -#: netbox/netbox/navigation/menu.py:212 netbox/templates/dcim/device.html:302 -#: netbox/templates/ipam/ipaddress.html:118 -#: netbox/templates/virtualization/virtualmachine.html:154 +#: netbox/navigation/menu.py:213 templates/dcim/device.html:302 +#: templates/ipam/ipaddress.html:118 +#: templates/virtualization/virtualmachine.html:154 msgid "Services" msgstr "" -#: netbox/netbox/navigation/menu.py:219 +#: netbox/navigation/menu.py:220 msgid "VPN" msgstr "" -#: netbox/netbox/navigation/menu.py:223 netbox/netbox/navigation/menu.py:225 -#: netbox/vpn/tables/tunnels.py:24 +#: netbox/navigation/menu.py:224 netbox/navigation/menu.py:226 +#: vpn/tables/tunnels.py:24 msgid "Tunnels" msgstr "" -#: netbox/netbox/navigation/menu.py:226 netbox/templates/vpn/tunnelgroup.html:8 +#: netbox/navigation/menu.py:227 templates/vpn/tunnelgroup.html:8 msgid "Tunnel Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:227 +#: netbox/navigation/menu.py:228 msgid "Tunnel Terminations" msgstr "" -#: netbox/netbox/navigation/menu.py:231 netbox/netbox/navigation/menu.py:233 -#: netbox/vpn/models/l2vpn.py:64 +#: netbox/navigation/menu.py:232 netbox/navigation/menu.py:234 +#: vpn/models/l2vpn.py:69 msgid "L2VPNs" msgstr "" -#: netbox/netbox/navigation/menu.py:240 +#: netbox/navigation/menu.py:241 msgid "IKE Proposals" msgstr "" -#: netbox/netbox/navigation/menu.py:241 -#: netbox/templates/vpn/ikeproposal.html:41 +#: netbox/navigation/menu.py:242 templates/vpn/ikeproposal.html:41 msgid "IKE Policies" msgstr "" -#: netbox/netbox/navigation/menu.py:242 +#: netbox/navigation/menu.py:243 msgid "IPSec Proposals" msgstr "" -#: netbox/netbox/navigation/menu.py:243 -#: netbox/templates/vpn/ipsecproposal.html:37 +#: netbox/navigation/menu.py:244 templates/vpn/ipsecproposal.html:37 msgid "IPSec Policies" msgstr "" -#: netbox/netbox/navigation/menu.py:244 netbox/templates/vpn/ikepolicy.html:38 -#: netbox/templates/vpn/ipsecpolicy.html:25 +#: netbox/navigation/menu.py:245 templates/vpn/ikepolicy.html:38 +#: templates/vpn/ipsecpolicy.html:25 msgid "IPSec Profiles" msgstr "" -#: netbox/netbox/navigation/menu.py:259 -#: netbox/templates/virtualization/virtualmachine.html:174 -#: netbox/templates/virtualization/virtualmachine/base.html:32 -#: netbox/templates/virtualization/virtualmachine_list.html:21 -#: netbox/virtualization/tables/virtualmachines.py:74 -#: netbox/virtualization/views.py:427 +#: netbox/navigation/menu.py:260 +#: templates/virtualization/virtualmachine.html:174 +#: templates/virtualization/virtualmachine/base.html:32 +#: templates/virtualization/virtualmachine_list.html:21 +#: virtualization/tables/virtualmachines.py:74 virtualization/views.py:416 msgid "Virtual Disks" msgstr "" -#: netbox/netbox/navigation/menu.py:266 +#: netbox/navigation/menu.py:267 msgid "Cluster Types" msgstr "" -#: netbox/netbox/navigation/menu.py:267 +#: netbox/navigation/menu.py:268 msgid "Cluster Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:281 +#: netbox/navigation/menu.py:282 msgid "Circuit Types" msgstr "" -#: netbox/netbox/navigation/menu.py:282 +#: netbox/navigation/menu.py:283 msgid "Circuit Terminations" msgstr "" -#: netbox/netbox/navigation/menu.py:286 netbox/netbox/navigation/menu.py:288 -#: netbox/templates/circuits/providernetwork.html:55 +#: netbox/navigation/menu.py:287 netbox/navigation/menu.py:289 +#: templates/circuits/providernetwork.html:55 msgid "Virtual Circuits" msgstr "" -#: netbox/netbox/navigation/menu.py:289 +#: netbox/navigation/menu.py:290 msgid "Virtual Circuit Types" msgstr "" -#: netbox/netbox/navigation/menu.py:290 +#: netbox/navigation/menu.py:291 msgid "Virtual Circuit Terminations" msgstr "" -#: netbox/netbox/navigation/menu.py:296 +#: netbox/navigation/menu.py:297 msgid "Circuit Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:297 -#: netbox/templates/circuits/circuit.html:76 -#: netbox/templates/circuits/virtualcircuit.html:69 +#: netbox/navigation/menu.py:298 templates/circuits/circuit.html:76 +#: templates/circuits/virtualcircuit.html:69 msgid "Group Assignments" msgstr "" -#: netbox/netbox/navigation/menu.py:301 netbox/netbox/navigation/menu.py:303 +#: netbox/navigation/menu.py:302 netbox/navigation/menu.py:304 msgid "Providers" msgstr "" -#: netbox/netbox/navigation/menu.py:304 -#: netbox/templates/circuits/provider.html:51 +#: netbox/navigation/menu.py:305 templates/circuits/provider.html:51 msgid "Provider Accounts" msgstr "" -#: netbox/netbox/navigation/menu.py:305 +#: netbox/navigation/menu.py:306 msgid "Provider Networks" msgstr "" -#: netbox/netbox/navigation/menu.py:319 +#: netbox/navigation/menu.py:320 msgid "Power Panels" msgstr "" -#: netbox/netbox/navigation/menu.py:330 +#: netbox/navigation/menu.py:331 msgid "Configurations" msgstr "" -#: netbox/netbox/navigation/menu.py:332 +#: netbox/navigation/menu.py:333 msgid "Config Contexts" msgstr "" -#: netbox/netbox/navigation/menu.py:333 +#: netbox/navigation/menu.py:334 msgid "Config Templates" msgstr "" -#: netbox/netbox/navigation/menu.py:340 netbox/netbox/navigation/menu.py:344 +#: netbox/navigation/menu.py:341 netbox/navigation/menu.py:345 msgid "Customization" msgstr "" -#: netbox/netbox/navigation/menu.py:346 -#: netbox/templates/dcim/device_edit.html:105 -#: netbox/templates/dcim/htmx/cable_edit.html:84 -#: netbox/templates/dcim/virtualchassis_add.html:35 -#: netbox/templates/dcim/virtualchassis_edit.html:44 -#: netbox/templates/generic/bulk_edit.html:76 -#: netbox/templates/htmx/form.html:19 netbox/templates/inc/filter_list.html:30 -#: netbox/templates/inc/panels/custom_fields.html:7 -#: netbox/templates/ipam/ipaddress_bulk_add.html:35 -#: netbox/templates/ipam/vlan_edit.html:71 +#: netbox/navigation/menu.py:347 templates/dcim/device_edit.html:105 +#: templates/dcim/htmx/cable_edit.html:84 +#: templates/dcim/virtualchassis_add.html:35 +#: templates/dcim/virtualchassis_edit.html:44 +#: templates/generic/bulk_edit.html:76 templates/htmx/form.html:19 +#: templates/inc/filter_list.html:30 templates/inc/panels/custom_fields.html:7 +#: templates/ipam/ipaddress_bulk_add.html:35 templates/ipam/vlan_edit.html:71 msgid "Custom Fields" msgstr "" -#: netbox/netbox/navigation/menu.py:347 +#: netbox/navigation/menu.py:348 msgid "Custom Field Choices" msgstr "" -#: netbox/netbox/navigation/menu.py:348 +#: netbox/navigation/menu.py:349 msgid "Custom Links" msgstr "" -#: netbox/netbox/navigation/menu.py:349 +#: netbox/navigation/menu.py:350 msgid "Export Templates" msgstr "" -#: netbox/netbox/navigation/menu.py:350 +#: netbox/navigation/menu.py:351 msgid "Saved Filters" msgstr "" -#: netbox/netbox/navigation/menu.py:352 +#: netbox/navigation/menu.py:352 +msgid "Table Configs" +msgstr "" + +#: netbox/navigation/menu.py:354 msgid "Image Attachments" msgstr "" -#: netbox/netbox/navigation/menu.py:370 +#: netbox/navigation/menu.py:372 msgid "Operations" msgstr "" -#: netbox/netbox/navigation/menu.py:374 +#: netbox/navigation/menu.py:376 msgid "Integrations" msgstr "" -#: netbox/netbox/navigation/menu.py:376 +#: netbox/navigation/menu.py:378 msgid "Data Sources" msgstr "" -#: netbox/netbox/navigation/menu.py:377 +#: netbox/navigation/menu.py:379 msgid "Event Rules" msgstr "" -#: netbox/netbox/navigation/menu.py:378 +#: netbox/navigation/menu.py:380 msgid "Webhooks" msgstr "" -#: netbox/netbox/navigation/menu.py:382 netbox/netbox/navigation/menu.py:386 -#: netbox/netbox/views/generic/feature_views.py:158 -#: netbox/templates/extras/report/base.html:37 -#: netbox/templates/extras/script/base.html:36 +#: netbox/navigation/menu.py:384 netbox/navigation/menu.py:388 +#: netbox/views/generic/feature_views.py:164 +#: templates/extras/report/base.html:37 templates/extras/script/base.html:36 msgid "Jobs" msgstr "" -#: netbox/netbox/navigation/menu.py:392 +#: netbox/navigation/menu.py:394 msgid "Logging" msgstr "" -#: netbox/netbox/navigation/menu.py:394 +#: netbox/navigation/menu.py:396 msgid "Notification Groups" msgstr "" -#: netbox/netbox/navigation/menu.py:395 +#: netbox/navigation/menu.py:397 msgid "Journal Entries" msgstr "" -#: netbox/netbox/navigation/menu.py:396 -#: netbox/templates/core/objectchange.html:9 -#: netbox/templates/core/objectchange_list.html:4 +#: netbox/navigation/menu.py:398 templates/core/objectchange.html:9 +#: templates/core/objectchange_list.html:4 msgid "Change Log" msgstr "" -#: netbox/netbox/navigation/menu.py:403 netbox/templates/inc/user_menu.html:29 +#: netbox/navigation/menu.py:405 templates/inc/user_menu.html:29 msgid "Admin" msgstr "" -#: netbox/netbox/navigation/menu.py:451 netbox/templates/account/base.html:27 -#: netbox/templates/inc/user_menu.html:52 +#: netbox/navigation/menu.py:453 templates/account/base.html:27 +#: templates/inc/user_menu.html:52 msgid "API Tokens" msgstr "" -#: netbox/netbox/navigation/menu.py:458 netbox/users/forms/model_forms.py:187 -#: netbox/users/forms/model_forms.py:195 netbox/users/forms/model_forms.py:242 -#: netbox/users/forms/model_forms.py:249 +#: netbox/navigation/menu.py:460 users/forms/model_forms.py:187 +#: users/forms/model_forms.py:195 users/forms/model_forms.py:242 +#: users/forms/model_forms.py:249 msgid "Permissions" msgstr "" -#: netbox/netbox/navigation/menu.py:466 netbox/netbox/navigation/menu.py:470 -#: netbox/templates/core/system.html:7 +#: netbox/navigation/menu.py:468 netbox/navigation/menu.py:472 +#: templates/core/system.html:7 msgid "System" msgstr "" -#: netbox/netbox/navigation/menu.py:475 netbox/netbox/navigation/menu.py:523 -#: netbox/templates/500.html:35 netbox/templates/account/preferences.html:22 -#: netbox/templates/core/plugin.html:13 -#: netbox/templates/core/plugin_list.html:7 -#: netbox/templates/core/plugin_list.html:12 +#: netbox/navigation/menu.py:477 netbox/navigation/menu.py:525 +#: templates/500.html:35 templates/account/preferences.html:22 +#: templates/core/plugin.html:13 templates/core/plugin_list.html:7 +#: templates/core/plugin_list.html:12 msgid "Plugins" msgstr "" -#: netbox/netbox/navigation/menu.py:480 +#: netbox/navigation/menu.py:482 msgid "Configuration History" msgstr "" -#: netbox/netbox/navigation/menu.py:486 netbox/templates/core/rq_task.html:8 -#: netbox/templates/core/rq_task_list.html:22 +#: netbox/navigation/menu.py:488 templates/core/rq_task.html:8 +#: templates/core/rq_task_list.html:22 msgid "Background Tasks" msgstr "" -#: netbox/netbox/plugins/navigation.py:48 -#: netbox/netbox/plugins/navigation.py:70 +#: netbox/plugins/navigation.py:48 netbox/plugins/navigation.py:70 msgid "Permissions must be passed as a tuple or list." msgstr "" -#: netbox/netbox/plugins/navigation.py:52 +#: netbox/plugins/navigation.py:52 msgid "Buttons must be passed as a tuple or list." msgstr "" -#: netbox/netbox/plugins/navigation.py:74 +#: netbox/plugins/navigation.py:74 msgid "Button color must be a choice within ButtonColorChoices." msgstr "" -#: netbox/netbox/plugins/registration.py:26 +#: netbox/plugins/registration.py:26 #, python-brace-format msgid "" "PluginTemplateExtension class {template_extension} was passed as an instance!" msgstr "" -#: netbox/netbox/plugins/registration.py:32 +#: netbox/plugins/registration.py:32 #, python-brace-format msgid "" "{template_extension} is not a subclass of netbox.plugins." "PluginTemplateExtension!" msgstr "" -#: netbox/netbox/plugins/registration.py:57 +#: netbox/plugins/registration.py:49 #, python-brace-format msgid "{item} must be an instance of netbox.plugins.PluginMenuItem" msgstr "" -#: netbox/netbox/plugins/registration.py:68 +#: netbox/plugins/registration.py:60 #, python-brace-format msgid "{menu_link} must be an instance of netbox.plugins.PluginMenuItem" msgstr "" -#: netbox/netbox/plugins/registration.py:73 +#: netbox/plugins/registration.py:65 #, python-brace-format msgid "{button} must be an instance of netbox.plugins.PluginMenuButton" msgstr "" -#: netbox/netbox/plugins/templates.py:37 +#: netbox/plugins/templates.py:42 msgid "extra_context must be a dictionary" msgstr "" -#: netbox/netbox/preferences.py:19 +#: netbox/preferences.py:19 msgid "HTMX Navigation" msgstr "" -#: netbox/netbox/preferences.py:24 +#: netbox/preferences.py:24 msgid "Enable dynamic UI navigation" msgstr "" -#: netbox/netbox/preferences.py:26 +#: netbox/preferences.py:26 msgid "Experimental feature" msgstr "" -#: netbox/netbox/preferences.py:29 +#: netbox/preferences.py:29 msgid "Language" msgstr "" -#: netbox/netbox/preferences.py:34 +#: netbox/preferences.py:34 msgid "Forces UI translation to the specified language" msgstr "" -#: netbox/netbox/preferences.py:36 +#: netbox/preferences.py:36 msgid "Support for translation has been disabled locally" msgstr "" -#: netbox/netbox/preferences.py:42 +#: netbox/preferences.py:42 msgid "Page length" msgstr "" -#: netbox/netbox/preferences.py:44 +#: netbox/preferences.py:44 msgid "The default number of objects to display per page" msgstr "" -#: netbox/netbox/preferences.py:48 +#: netbox/preferences.py:48 msgid "Paginator placement" msgstr "" -#: netbox/netbox/preferences.py:50 +#: netbox/preferences.py:50 msgid "Bottom" msgstr "" -#: netbox/netbox/preferences.py:51 +#: netbox/preferences.py:51 msgid "Top" msgstr "" -#: netbox/netbox/preferences.py:52 +#: netbox/preferences.py:52 msgid "Both" msgstr "" -#: netbox/netbox/preferences.py:55 +#: netbox/preferences.py:55 msgid "Where the paginator controls will be displayed relative to a table" msgstr "" -#: netbox/netbox/preferences.py:60 +#: netbox/preferences.py:60 msgid "Data format" msgstr "" -#: netbox/netbox/preferences.py:65 +#: netbox/preferences.py:65 msgid "The preferred syntax for displaying generic data within the UI" msgstr "" -#: netbox/netbox/registry.py:14 +#: netbox/registry.py:14 #, python-brace-format msgid "Invalid store: {key}" msgstr "" -#: netbox/netbox/registry.py:17 +#: netbox/registry.py:17 msgid "Cannot add stores to registry after initialization" msgstr "" -#: netbox/netbox/registry.py:20 +#: netbox/registry.py:20 msgid "Cannot delete stores from registry" msgstr "" -#: netbox/netbox/settings.py:758 +#: netbox/settings.py:782 msgid "Czech" msgstr "" -#: netbox/netbox/settings.py:759 +#: netbox/settings.py:783 msgid "Danish" msgstr "" -#: netbox/netbox/settings.py:760 +#: netbox/settings.py:784 msgid "German" msgstr "" -#: netbox/netbox/settings.py:761 +#: netbox/settings.py:785 msgid "English" msgstr "" -#: netbox/netbox/settings.py:762 +#: netbox/settings.py:786 msgid "Spanish" msgstr "" -#: netbox/netbox/settings.py:763 +#: netbox/settings.py:787 msgid "French" msgstr "" -#: netbox/netbox/settings.py:764 +#: netbox/settings.py:788 msgid "Italian" msgstr "" -#: netbox/netbox/settings.py:765 +#: netbox/settings.py:789 msgid "Japanese" msgstr "" -#: netbox/netbox/settings.py:766 +#: netbox/settings.py:790 msgid "Dutch" msgstr "" -#: netbox/netbox/settings.py:767 +#: netbox/settings.py:791 msgid "Polish" msgstr "" -#: netbox/netbox/settings.py:768 +#: netbox/settings.py:792 msgid "Portuguese" msgstr "" -#: netbox/netbox/settings.py:769 +#: netbox/settings.py:793 msgid "Russian" msgstr "" -#: netbox/netbox/settings.py:770 +#: netbox/settings.py:794 msgid "Turkish" msgstr "" -#: netbox/netbox/settings.py:771 +#: netbox/settings.py:795 msgid "Ukrainian" msgstr "" -#: netbox/netbox/settings.py:772 +#: netbox/settings.py:796 msgid "Chinese" msgstr "" -#: netbox/netbox/tables/columns.py:177 +#: netbox/tables/columns.py:178 msgid "Select all" msgstr "" -#: netbox/netbox/tables/columns.py:190 +#: netbox/tables/columns.py:191 msgid "Toggle all" msgstr "" -#: netbox/netbox/tables/columns.py:302 +#: netbox/tables/columns.py:307 msgid "Toggle Dropdown" msgstr "" -#: netbox/netbox/tables/columns.py:575 netbox/templates/core/job.html:53 +#: netbox/tables/columns.py:580 templates/core/job.html:53 msgid "Error" msgstr "" -#: netbox/netbox/tables/tables.py:59 +#: netbox/tables/tables.py:59 #, python-brace-format msgid "No {model_name} found" msgstr "" -#: netbox/netbox/tables/tables.py:252 -#: netbox/templates/generic/bulk_import.html:117 +#: netbox/tables/tables.py:278 templates/generic/bulk_import.html:117 msgid "Field" msgstr "" -#: netbox/netbox/tables/tables.py:255 +#: netbox/tables/tables.py:281 msgid "Value" msgstr "" -#: netbox/netbox/tests/dummy_plugin/navigation.py:29 +#: netbox/tests/dummy_plugin/navigation.py:29 msgid "Dummy Plugin" msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:116 +#: netbox/views/generic/bulk_views.py:117 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " "{error}" msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:425 +#: netbox/views/generic/bulk_views.py:427 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:714 -#: netbox/netbox/views/generic/bulk_views.py:915 -#: netbox/netbox/views/generic/bulk_views.py:963 +#: netbox/views/generic/bulk_views.py:716 +#: netbox/views/generic/bulk_views.py:917 +#: netbox/views/generic/bulk_views.py:965 #, python-brace-format msgid "No {object_type} were selected." msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:793 +#: netbox/views/generic/bulk_views.py:795 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "" -#: netbox/netbox/views/generic/bulk_views.py:893 +#: netbox/views/generic/bulk_views.py:895 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "" -#: netbox/netbox/views/generic/feature_views.py:40 +#: netbox/views/generic/feature_views.py:46 msgid "Changelog" msgstr "" -#: netbox/netbox/views/generic/feature_views.py:93 +#: netbox/views/generic/feature_views.py:99 msgid "Journal" msgstr "" -#: netbox/netbox/views/generic/feature_views.py:212 +#: netbox/views/generic/feature_views.py:218 msgid "Unable to synchronize data: No data file set." msgstr "" -#: netbox/netbox/views/generic/feature_views.py:216 +#: netbox/views/generic/feature_views.py:222 #, python-brace-format msgid "Synchronized data for {object_type} {object}." msgstr "" -#: netbox/netbox/views/generic/feature_views.py:241 +#: netbox/views/generic/feature_views.py:247 #, python-brace-format msgid "Synced {count} {object_type}" msgstr "" -#: netbox/netbox/views/generic/object_views.py:109 +#: netbox/views/generic/object_views.py:110 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "" -#: netbox/netbox/views/misc.py:46 +#: netbox/views/misc.py:46 msgid "" "There was an error loading the dashboard configuration. A default dashboard " "is in use." msgstr "" -#: netbox/templates/403.html:4 +#: templates/403.html:4 msgid "Access Denied" msgstr "" -#: netbox/templates/403.html:9 +#: templates/403.html:9 msgid "You do not have permission to access this page" msgstr "" -#: netbox/templates/404.html:4 +#: templates/404.html:4 msgid "Page Not Found" msgstr "" -#: netbox/templates/404.html:9 +#: templates/404.html:9 msgid "The requested page does not exist" msgstr "" -#: netbox/templates/500.html:7 netbox/templates/500.html:18 +#: templates/500.html:7 templates/500.html:18 msgid "Server Error" msgstr "" -#: netbox/templates/500.html:23 +#: templates/500.html:23 msgid "There was a problem with your request. Please contact an administrator" msgstr "" -#: netbox/templates/500.html:28 +#: templates/500.html:28 msgid "The complete exception is provided below" msgstr "" -#: netbox/templates/500.html:33 netbox/templates/core/system.html:40 +#: templates/500.html:33 templates/core/system.html:40 msgid "Python version" msgstr "" -#: netbox/templates/500.html:34 +#: templates/500.html:34 msgid "NetBox version" msgstr "" -#: netbox/templates/500.html:36 +#: templates/500.html:36 msgid "None installed" msgstr "" -#: netbox/templates/500.html:39 +#: templates/500.html:39 msgid "If further assistance is required, please post to the" msgstr "" -#: netbox/templates/500.html:39 +#: templates/500.html:39 msgid "NetBox discussion forum" msgstr "" -#: netbox/templates/500.html:39 +#: templates/500.html:39 msgid "on GitHub" msgstr "" -#: netbox/templates/500.html:42 netbox/templates/base/40x.html:17 +#: templates/500.html:42 templates/base/40x.html:17 msgid "Home Page" msgstr "" -#: netbox/templates/account/base.html:7 netbox/templates/inc/user_menu.html:40 -#: netbox/vpn/forms/bulk_edit.py:255 netbox/vpn/forms/filtersets.py:194 -#: netbox/vpn/forms/model_forms.py:382 -msgid "Profile" -msgstr "" - -#: netbox/templates/account/base.html:13 -#: netbox/templates/account/notifications.html:7 -#: netbox/templates/inc/user_menu.html:15 +#: templates/account/base.html:13 templates/account/notifications.html:7 +#: templates/inc/user_menu.html:15 msgid "Notifications" msgstr "" -#: netbox/templates/account/base.html:16 -#: netbox/templates/account/subscriptions.html:7 -#: netbox/templates/inc/user_menu.html:46 +#: templates/account/base.html:16 templates/account/subscriptions.html:7 +#: templates/inc/user_menu.html:46 msgid "Subscriptions" msgstr "" -#: netbox/templates/account/base.html:19 netbox/templates/inc/user_menu.html:49 +#: templates/account/base.html:19 templates/inc/user_menu.html:49 msgid "Preferences" msgstr "" -#: netbox/templates/account/password.html:5 +#: templates/account/password.html:5 msgid "Change Password" msgstr "" -#: netbox/templates/account/password.html:19 -#: netbox/templates/account/preferences.html:77 -#: netbox/templates/core/configrevision_restore.html:63 -#: netbox/templates/dcim/devicebay_populate.html:34 -#: netbox/templates/dcim/virtualchassis_add_member.html:26 -#: netbox/templates/dcim/virtualchassis_edit.html:107 -#: netbox/templates/extras/object_journal.html:26 -#: netbox/templates/extras/script.html:38 -#: netbox/templates/generic/bulk_add_component.html:67 -#: netbox/templates/generic/bulk_delete.html:65 -#: netbox/templates/generic/bulk_edit.html:106 -#: netbox/templates/generic/bulk_import.html:56 -#: netbox/templates/generic/bulk_import.html:78 -#: netbox/templates/generic/bulk_import.html:100 -#: netbox/templates/generic/bulk_remove.html:62 -#: netbox/templates/generic/bulk_rename.html:63 -#: netbox/templates/generic/confirmation_form.html:19 -#: netbox/templates/generic/object_edit.html:72 -#: netbox/templates/htmx/delete_form.html:53 -#: netbox/templates/htmx/delete_form.html:55 -#: netbox/templates/htmx/quick_add.html:21 -#: netbox/templates/ipam/ipaddress_assign.html:28 -#: netbox/templates/virtualization/cluster_add_devices.html:30 +#: templates/account/password.html:19 templates/account/preferences.html:77 +#: templates/core/configrevision_restore.html:63 +#: templates/dcim/devicebay_populate.html:34 +#: templates/dcim/virtualchassis_add_member.html:26 +#: templates/dcim/virtualchassis_edit.html:107 +#: templates/extras/object_journal.html:26 templates/extras/script.html:38 +#: templates/generic/bulk_add_component.html:67 +#: templates/generic/bulk_delete.html:65 templates/generic/bulk_edit.html:106 +#: templates/generic/bulk_import.html:56 templates/generic/bulk_import.html:78 +#: templates/generic/bulk_import.html:100 templates/generic/bulk_remove.html:62 +#: templates/generic/bulk_rename.html:63 +#: templates/generic/confirmation_form.html:19 +#: templates/generic/object_edit.html:72 templates/htmx/delete_form.html:53 +#: templates/htmx/delete_form.html:55 templates/htmx/quick_add.html:21 +#: templates/ipam/ipaddress_assign.html:28 +#: templates/virtualization/cluster_add_devices.html:30 msgid "Cancel" msgstr "" -#: netbox/templates/account/password.html:20 -#: netbox/templates/account/preferences.html:78 -#: netbox/templates/dcim/devicebay_populate.html:35 -#: netbox/templates/dcim/virtualchassis_add_member.html:28 -#: netbox/templates/dcim/virtualchassis_edit.html:109 -#: netbox/templates/extras/dashboard/widget_add.html:26 -#: netbox/templates/extras/dashboard/widget_config.html:19 -#: netbox/templates/extras/object_journal.html:27 -#: netbox/templates/generic/object_edit.html:75 -#: netbox/utilities/templates/helpers/applied_filters.html:16 -#: netbox/utilities/templates/helpers/table_config_form.html:40 +#: templates/account/password.html:20 templates/account/preferences.html:78 +#: templates/dcim/devicebay_populate.html:35 +#: templates/dcim/virtualchassis_add_member.html:28 +#: templates/dcim/virtualchassis_edit.html:109 +#: templates/extras/dashboard/widget_add.html:26 +#: templates/extras/dashboard/widget_config.html:19 +#: templates/extras/object_journal.html:27 +#: templates/generic/object_edit.html:75 +#: utilities/templates/helpers/applied_filters.html:16 +#: utilities/templates/helpers/table_config_form.html:40 msgid "Save" msgstr "" -#: netbox/templates/account/preferences.html:34 +#: templates/account/preferences.html:34 msgid "Table Configurations" msgstr "" -#: netbox/templates/account/preferences.html:39 +#: templates/account/preferences.html:39 msgid "Clear table preferences" msgstr "" -#: netbox/templates/account/preferences.html:47 +#: templates/account/preferences.html:47 msgid "Toggle All" msgstr "" -#: netbox/templates/account/preferences.html:49 +#: templates/account/preferences.html:49 templates/extras/tableconfig.html:25 msgid "Table" msgstr "" -#: netbox/templates/account/preferences.html:50 -msgid "Ordering" -msgstr "" - -#: netbox/templates/account/preferences.html:51 +#: templates/account/preferences.html:51 msgid "Columns" msgstr "" -#: netbox/templates/account/preferences.html:71 -#: netbox/templates/dcim/cable_trace.html:113 -#: netbox/templates/extras/object_configcontext.html:43 +#: templates/account/preferences.html:71 templates/dcim/cable_trace.html:113 +#: templates/extras/object_configcontext.html:43 msgid "None found" msgstr "" -#: netbox/templates/account/profile.html:6 +#: templates/account/profile.html:6 msgid "User Profile" msgstr "" -#: netbox/templates/account/profile.html:12 +#: templates/account/profile.html:12 msgid "Account Details" msgstr "" -#: netbox/templates/account/profile.html:29 -#: netbox/templates/tenancy/contact.html:43 netbox/templates/users/user.html:25 -#: netbox/tenancy/forms/bulk_edit.py:109 +#: templates/account/profile.html:29 templates/tenancy/contact.html:53 +#: templates/users/user.html:25 tenancy/forms/bulk_edit.py:116 msgid "Email" msgstr "" -#: netbox/templates/account/profile.html:33 netbox/templates/users/user.html:29 +#: templates/account/profile.html:33 templates/users/user.html:29 msgid "Account Created" msgstr "" -#: netbox/templates/account/profile.html:37 netbox/templates/users/user.html:33 +#: templates/account/profile.html:37 templates/users/user.html:33 msgid "Last Login" msgstr "" -#: netbox/templates/account/profile.html:41 netbox/templates/users/user.html:45 +#: templates/account/profile.html:41 templates/users/user.html:45 msgid "Superuser" msgstr "" -#: netbox/templates/account/profile.html:45 -#: netbox/templates/inc/user_menu.html:31 netbox/templates/users/user.html:41 +#: templates/account/profile.html:45 templates/inc/user_menu.html:31 +#: templates/users/user.html:41 msgid "Staff" msgstr "" -#: netbox/templates/account/profile.html:53 -#: netbox/templates/users/objectpermission.html:82 -#: netbox/templates/users/user.html:53 +#: templates/account/profile.html:53 templates/users/objectpermission.html:82 +#: templates/users/user.html:53 msgid "Assigned Groups" msgstr "" -#: netbox/templates/account/profile.html:58 -#: netbox/templates/circuits/circuit_terminations_swap.html:18 -#: netbox/templates/circuits/circuit_terminations_swap.html:26 -#: netbox/templates/circuits/circuittermination.html:34 -#: netbox/templates/circuits/inc/circuit_termination.html:68 -#: netbox/templates/core/objectchange.html:124 -#: netbox/templates/core/objectchange.html:142 -#: netbox/templates/dcim/devicebay.html:59 -#: netbox/templates/dcim/inc/panels/inventory_items.html:45 -#: netbox/templates/dcim/interface.html:353 -#: netbox/templates/dcim/modulebay.html:80 -#: netbox/templates/extras/configcontext.html:70 -#: netbox/templates/extras/eventrule.html:66 -#: netbox/templates/extras/htmx/script_result.html:60 -#: netbox/templates/extras/webhook.html:65 -#: netbox/templates/extras/webhook.html:75 -#: netbox/templates/inc/panel_table.html:13 -#: netbox/templates/inc/panels/comments.html:10 -#: netbox/templates/inc/panels/related_objects.html:23 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:56 -#: netbox/templates/users/group.html:34 netbox/templates/users/group.html:44 -#: netbox/templates/users/objectpermission.html:77 -#: netbox/templates/users/objectpermission.html:87 -#: netbox/templates/users/user.html:58 netbox/templates/users/user.html:68 +#: templates/account/profile.html:58 +#: templates/circuits/circuit_terminations_swap.html:18 +#: templates/circuits/circuit_terminations_swap.html:26 +#: templates/circuits/circuittermination.html:34 +#: templates/circuits/inc/circuit_termination.html:68 +#: templates/core/objectchange.html:124 templates/core/objectchange.html:142 +#: templates/dcim/devicebay.html:59 +#: templates/dcim/inc/panels/inventory_items.html:45 +#: templates/dcim/interface.html:353 templates/dcim/modulebay.html:80 +#: templates/dcim/moduletype.html:90 templates/extras/configcontext.html:70 +#: templates/extras/configtemplate.html:77 templates/extras/eventrule.html:66 +#: templates/extras/exporttemplate.html:93 +#: templates/extras/htmx/script_result.html:60 templates/extras/webhook.html:65 +#: templates/extras/webhook.html:75 templates/inc/panel_table.html:13 +#: templates/inc/panels/comments.html:10 +#: templates/inc/panels/related_objects.html:23 +#: templates/ipam/inc/panels/fhrp_groups.html:56 templates/users/group.html:34 +#: templates/users/group.html:44 templates/users/objectpermission.html:77 +#: templates/users/objectpermission.html:87 templates/users/user.html:58 +#: templates/users/user.html:68 msgid "None" msgstr "" -#: netbox/templates/account/profile.html:68 netbox/templates/users/user.html:78 +#: templates/account/profile.html:68 templates/users/user.html:78 msgid "Recent Activity" msgstr "" -#: netbox/templates/account/token.html:8 -#: netbox/templates/account/token_list.html:6 +#: templates/account/token.html:8 templates/account/token_list.html:6 msgid "My API Tokens" msgstr "" -#: netbox/templates/account/token.html:11 -#: netbox/templates/account/token.html:19 netbox/templates/users/token.html:6 -#: netbox/templates/users/token.html:14 netbox/users/forms/filtersets.py:120 +#: templates/account/token.html:11 templates/account/token.html:19 +#: templates/users/token.html:6 templates/users/token.html:14 +#: users/forms/filtersets.py:120 msgid "Token" msgstr "" -#: netbox/templates/account/token.html:39 netbox/templates/users/token.html:31 -#: netbox/users/forms/bulk_edit.py:107 +#: templates/account/token.html:39 templates/users/token.html:31 +#: users/forms/bulk_edit.py:107 msgid "Write enabled" msgstr "" -#: netbox/templates/account/token.html:51 netbox/templates/users/token.html:43 +#: templates/account/token.html:51 templates/users/token.html:43 msgid "Last used" msgstr "" -#: netbox/templates/account/token_list.html:12 +#: templates/account/token_list.html:12 msgid "Add a Token" msgstr "" -#: netbox/templates/base/base.html:23 netbox/templates/home.html:27 +#: templates/base/base.html:24 templates/home.html:27 msgid "Home" msgstr "" -#: netbox/templates/base/layout.html:25 +#: templates/base/layout.html:25 msgid "NetBox Motif" msgstr "" -#: netbox/templates/base/layout.html:38 netbox/templates/base/layout.html:39 -#: netbox/templates/login.html:14 netbox/templates/login.html:15 +#: templates/base/layout.html:38 templates/base/layout.html:39 +#: templates/login.html:14 templates/login.html:15 msgid "NetBox Logo" msgstr "" -#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +#: templates/base/layout.html:60 templates/base/layout.html:61 msgid "Get" msgstr "" -#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 +#: templates/base/layout.html:161 templates/base/layout.html:162 msgid "Docs" msgstr "" -#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 -#: netbox/templates/rest_framework/api.html:10 +#: templates/base/layout.html:167 templates/base/layout.html:168 +#: templates/rest_framework/api.html:10 msgid "REST API" msgstr "" -#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 +#: templates/base/layout.html:173 templates/base/layout.html:174 msgid "REST API documentation" msgstr "" -#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 +#: templates/base/layout.html:180 templates/base/layout.html:181 msgid "GraphQL API" msgstr "" -#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 +#: templates/base/layout.html:196 templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "" -#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 +#: templates/base/layout.html:205 templates/base/layout.html:206 msgid "Source Code" msgstr "" -#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 +#: templates/base/layout.html:211 templates/base/layout.html:212 msgid "Community" msgstr "" -#: netbox/templates/circuits/circuit.html:57 +#: templates/circuits/circuit.html:57 msgid "Install Date" msgstr "" -#: netbox/templates/circuits/circuit.html:61 +#: templates/circuits/circuit.html:61 msgid "Termination Date" msgstr "" -#: netbox/templates/circuits/circuit.html:80 -#: netbox/templates/circuits/virtualcircuit.html:73 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:15 +#: templates/circuits/circuit.html:80 templates/circuits/virtualcircuit.html:73 +#: templates/ipam/inc/panels/fhrp_groups.html:15 msgid "Assign Group" msgstr "" -#: netbox/templates/circuits/circuit_terminations_swap.html:4 +#: templates/circuits/circuit_terminations_swap.html:4 msgid "Swap Circuit Terminations" msgstr "" -#: netbox/templates/circuits/circuit_terminations_swap.html:8 +#: templates/circuits/circuit_terminations_swap.html:8 #, python-format msgid "Swap these terminations for circuit %(circuit)s?" msgstr "" -#: netbox/templates/circuits/circuit_terminations_swap.html:14 +#: templates/circuits/circuit_terminations_swap.html:14 msgid "A side" msgstr "" -#: netbox/templates/circuits/circuit_terminations_swap.html:22 +#: templates/circuits/circuit_terminations_swap.html:22 msgid "Z side" msgstr "" -#: netbox/templates/circuits/circuitgroup.html:16 +#: templates/circuits/circuitgroup.html:16 msgid "Assign Circuit" msgstr "" -#: netbox/templates/circuits/circuitgroupassignment.html:19 +#: templates/circuits/circuitgroupassignment.html:19 msgid "Circuit Group Assignment" msgstr "" -#: netbox/templates/circuits/circuittype.html:10 +#: templates/circuits/circuittype.html:10 msgid "Add Circuit" msgstr "" -#: netbox/templates/circuits/circuittype.html:19 +#: templates/circuits/circuittype.html:19 msgid "Circuit Type" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination.html:10 -#: netbox/templates/dcim/manufacturer.html:11 -#: netbox/templates/generic/bulk_add_component.html:22 -#: netbox/templates/users/objectpermission.html:38 -#: netbox/utilities/templates/buttons/add.html:4 -#: netbox/utilities/templates/helpers/table_config_form.html:20 +#: templates/circuits/inc/circuit_termination.html:10 +#: templates/dcim/manufacturer.html:11 +#: templates/extras/tableconfig_edit.html:29 +#: templates/generic/bulk_add_component.html:22 +#: templates/users/objectpermission.html:38 +#: utilities/templates/buttons/add.html:4 +#: utilities/templates/helpers/table_config_form.html:20 msgid "Add" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination.html:15 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:37 -#: netbox/templates/dcim/inc/panels/inventory_items.html:32 -#: netbox/templates/dcim/powerpanel.html:56 -#: netbox/templates/extras/script_list.html:30 -#: netbox/templates/generic/object_edit.html:47 -#: netbox/templates/ipam/inc/ipaddress_edit_header.html:7 -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:43 -#: netbox/utilities/templates/buttons/edit.html:3 +#: templates/circuits/inc/circuit_termination.html:15 +#: templates/circuits/inc/circuit_termination_fields.html:37 +#: templates/dcim/inc/panels/inventory_items.html:32 +#: templates/dcim/powerpanel.html:56 templates/extras/script_list.html:30 +#: templates/generic/object_edit.html:47 +#: templates/ipam/inc/ipaddress_edit_header.html:7 +#: templates/ipam/inc/panels/fhrp_groups.html:43 +#: utilities/templates/buttons/edit.html:3 msgid "Edit" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination.html:18 +#: templates/circuits/inc/circuit_termination.html:18 msgid "Swap" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:5 +#: templates/circuits/inc/circuit_termination.html:23 +#: templates/dcim/inc/panels/inventory_items.html:37 +#: templates/dcim/powerpanel.html:66 templates/extras/script_list.html:35 +#: templates/generic/bulk_delete.html:20 templates/generic/bulk_delete.html:66 +#: templates/generic/object_delete.html:19 templates/htmx/delete_form.html:57 +#: templates/ipam/inc/panels/fhrp_groups.html:48 +#: templates/users/objectpermission.html:46 +#: utilities/templates/buttons/delete.html:11 +msgid "Delete" +msgstr "" + +#: templates/circuits/inc/circuit_termination_fields.html:5 msgid "Termination point" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:20 -#: netbox/templates/dcim/consoleport.html:59 -#: netbox/templates/dcim/consoleserverport.html:60 -#: netbox/templates/dcim/powerfeed.html:114 +#: templates/circuits/inc/circuit_termination_fields.html:20 +#: templates/dcim/consoleport.html:59 templates/dcim/consoleserverport.html:60 +#: templates/dcim/powerfeed.html:114 msgid "Marked as connected" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:22 +#: templates/circuits/inc/circuit_termination_fields.html:22 msgid "to" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:32 -#: netbox/templates/circuits/inc/circuit_termination_fields.html:33 -#: netbox/templates/dcim/frontport.html:80 -#: netbox/templates/dcim/inc/cable_termination.html:27 -#: netbox/templates/dcim/inc/cable_termination.html:51 -#: netbox/templates/dcim/inc/cable_termination.html:71 -#: netbox/templates/dcim/inc/connection_endpoints.html:7 -#: netbox/templates/dcim/interface.html:211 -#: netbox/templates/dcim/rearport.html:76 +#: templates/circuits/inc/circuit_termination_fields.html:32 +#: templates/circuits/inc/circuit_termination_fields.html:33 +#: templates/dcim/frontport.html:80 +#: templates/dcim/inc/cable_termination.html:27 +#: templates/dcim/inc/cable_termination.html:51 +#: templates/dcim/inc/cable_termination.html:71 +#: templates/dcim/inc/connection_endpoints.html:7 +#: templates/dcim/interface.html:211 templates/dcim/rearport.html:76 msgid "Trace" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:36 +#: templates/circuits/inc/circuit_termination_fields.html:36 msgid "Edit cable" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:41 +#: templates/circuits/inc/circuit_termination_fields.html:41 msgid "Remove cable" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:42 -#: netbox/templates/dcim/bulk_disconnect.html:5 -#: netbox/templates/dcim/device/consoleports.html:12 -#: netbox/templates/dcim/device/consoleserverports.html:12 -#: netbox/templates/dcim/device/frontports.html:12 -#: netbox/templates/dcim/device/interfaces.html:16 -#: netbox/templates/dcim/device/poweroutlets.html:12 -#: netbox/templates/dcim/device/powerports.html:12 -#: netbox/templates/dcim/device/rearports.html:12 -#: netbox/templates/dcim/powerpanel.html:61 +#: templates/circuits/inc/circuit_termination_fields.html:42 +#: templates/dcim/bulk_disconnect.html:5 +#: templates/dcim/device/consoleports.html:12 +#: templates/dcim/device/consoleserverports.html:12 +#: templates/dcim/device/frontports.html:12 +#: templates/dcim/device/interfaces.html:16 +#: templates/dcim/device/poweroutlets.html:12 +#: templates/dcim/device/powerports.html:12 +#: templates/dcim/device/rearports.html:12 templates/dcim/powerpanel.html:61 msgid "Disconnect" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:49 -#: netbox/templates/dcim/consoleport.html:69 -#: netbox/templates/dcim/consoleserverport.html:70 -#: netbox/templates/dcim/frontport.html:102 -#: netbox/templates/dcim/interface.html:237 -#: netbox/templates/dcim/interface.html:257 -#: netbox/templates/dcim/powerfeed.html:127 -#: netbox/templates/dcim/poweroutlet.html:81 -#: netbox/templates/dcim/poweroutlet.html:82 -#: netbox/templates/dcim/powerport.html:73 -#: netbox/templates/dcim/rearport.html:98 +#: templates/circuits/inc/circuit_termination_fields.html:49 +#: templates/dcim/consoleport.html:69 templates/dcim/consoleserverport.html:70 +#: templates/dcim/frontport.html:102 templates/dcim/interface.html:237 +#: templates/dcim/interface.html:257 templates/dcim/powerfeed.html:127 +#: templates/dcim/poweroutlet.html:85 templates/dcim/poweroutlet.html:86 +#: templates/dcim/powerport.html:73 templates/dcim/rearport.html:98 msgid "Connect" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:65 +#: templates/circuits/inc/circuit_termination_fields.html:65 msgid "Downstream" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:66 +#: templates/circuits/inc/circuit_termination_fields.html:66 msgid "Upstream" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:75 +#: templates/circuits/inc/circuit_termination_fields.html:75 msgid "Cross-Connect" msgstr "" -#: netbox/templates/circuits/inc/circuit_termination_fields.html:79 +#: templates/circuits/inc/circuit_termination_fields.html:79 msgid "Patch Panel/Port" msgstr "" -#: netbox/templates/circuits/provider.html:11 +#: templates/circuits/provider.html:11 msgid "Add circuit" msgstr "" -#: netbox/templates/circuits/provideraccount.html:17 +#: templates/circuits/provideraccount.html:17 msgid "Provider Account" msgstr "" -#: netbox/templates/circuits/providernetwork.html:59 +#: templates/circuits/providernetwork.html:59 msgid "Add a Virtual Circuit" msgstr "" -#: netbox/templates/circuits/virtualcircuit.html:91 -#: netbox/templates/vpn/tunnel.html:9 +#: templates/circuits/virtualcircuit.html:91 templates/vpn/tunnel.html:9 msgid "Add Termination" msgstr "" -#: netbox/templates/circuits/virtualcircuittermination.html:23 +#: templates/circuits/virtualcircuittermination.html:23 msgid "Virtual Circuit Termination" msgstr "" -#: netbox/templates/circuits/virtualcircuittype.html:10 +#: templates/circuits/virtualcircuittype.html:10 msgid "Add Virtual Circuit" msgstr "" -#: netbox/templates/circuits/virtualcircuittype.html:19 +#: templates/circuits/virtualcircuittype.html:19 msgid "Virtual Circuit Type" msgstr "" -#: netbox/templates/core/configrevision.html:35 +#: templates/core/configrevision.html:35 msgid "Configuration Data" msgstr "" -#: netbox/templates/core/configrevision.html:40 +#: templates/core/configrevision.html:40 msgid "Comment" msgstr "" -#: netbox/templates/core/configrevision_restore.html:8 -#: netbox/templates/core/configrevision_restore.html:25 -#: netbox/templates/core/configrevision_restore.html:64 +#: templates/core/configrevision_restore.html:8 +#: templates/core/configrevision_restore.html:25 +#: templates/core/configrevision_restore.html:64 msgid "Restore" msgstr "" -#: netbox/templates/core/configrevision_restore.html:36 +#: templates/core/configrevision_restore.html:36 msgid "Parameter" msgstr "" -#: netbox/templates/core/configrevision_restore.html:37 +#: templates/core/configrevision_restore.html:37 msgid "Current Value" msgstr "" -#: netbox/templates/core/configrevision_restore.html:38 +#: templates/core/configrevision_restore.html:38 msgid "New Value" msgstr "" -#: netbox/templates/core/configrevision_restore.html:50 +#: templates/core/configrevision_restore.html:50 msgid "Changed" msgstr "" -#: netbox/templates/core/datafile.html:42 netbox/templates/ipam/iprange.html:25 -#: netbox/templates/virtualization/virtualdisk.html:29 -#: netbox/virtualization/tables/virtualmachines.py:169 +#: templates/core/datafile.html:42 templates/ipam/iprange.html:25 +#: templates/virtualization/virtualdisk.html:29 +#: virtualization/tables/virtualmachines.py:169 msgid "Size" msgstr "" -#: netbox/templates/core/datafile.html:43 +#: templates/core/datafile.html:43 msgid "bytes" msgstr "" -#: netbox/templates/core/datafile.html:46 +#: templates/core/datafile.html:46 msgid "SHA256 Hash" msgstr "" -#: netbox/templates/core/datasource.html:14 -#: netbox/templates/core/datasource.html:20 -#: netbox/utilities/templates/buttons/sync.html:5 -msgid "Sync" +#: templates/core/datafile.html:55 +msgid "Content" msgstr "" -#: netbox/templates/core/datasource.html:50 +#: templates/core/datasource.html:54 msgid "Last synced" msgstr "" -#: netbox/templates/core/datasource.html:84 +#: templates/core/datasource.html:88 msgid "Backend" msgstr "" -#: netbox/templates/core/datasource.html:99 +#: templates/core/datasource.html:103 msgid "No parameters defined" msgstr "" -#: netbox/templates/core/datasource.html:114 -msgid "Files" -msgstr "" - -#: netbox/templates/core/inc/config_data.html:7 +#: templates/core/inc/config_data.html:7 msgid "Rack elevations" msgstr "" -#: netbox/templates/core/inc/config_data.html:10 +#: templates/core/inc/config_data.html:10 msgid "Default unit height" msgstr "" -#: netbox/templates/core/inc/config_data.html:14 +#: templates/core/inc/config_data.html:14 msgid "Default unit width" msgstr "" -#: netbox/templates/core/inc/config_data.html:20 +#: templates/core/inc/config_data.html:20 msgid "Power feeds" msgstr "" -#: netbox/templates/core/inc/config_data.html:23 +#: templates/core/inc/config_data.html:23 msgid "Default voltage" msgstr "" -#: netbox/templates/core/inc/config_data.html:27 +#: templates/core/inc/config_data.html:27 msgid "Default amperage" msgstr "" -#: netbox/templates/core/inc/config_data.html:31 +#: templates/core/inc/config_data.html:31 msgid "Default max utilization" msgstr "" -#: netbox/templates/core/inc/config_data.html:40 +#: templates/core/inc/config_data.html:40 msgid "Enforce global unique" msgstr "" -#: netbox/templates/core/inc/config_data.html:83 +#: templates/core/inc/config_data.html:83 msgid "Paginate count" msgstr "" -#: netbox/templates/core/inc/config_data.html:87 +#: templates/core/inc/config_data.html:87 msgid "Max page size" msgstr "" -#: netbox/templates/core/inc/config_data.html:114 +#: templates/core/inc/config_data.html:114 msgid "User preferences" msgstr "" -#: netbox/templates/core/inc/config_data.html:141 +#: templates/core/inc/config_data.html:141 msgid "Job retention" msgstr "" -#: netbox/templates/core/job.html:35 netbox/templates/core/rq_task.html:12 -#: netbox/templates/core/rq_task.html:49 netbox/templates/core/rq_task.html:58 +#: templates/core/job.html:35 templates/core/rq_task.html:12 +#: templates/core/rq_task.html:49 templates/core/rq_task.html:58 msgid "Job" msgstr "" -#: netbox/templates/core/job.html:58 -#: netbox/templates/extras/journalentry.html:26 +#: templates/core/job.html:58 templates/extras/journalentry.html:26 msgid "Created By" msgstr "" -#: netbox/templates/core/job.html:66 +#: templates/core/job.html:66 msgid "Scheduling" msgstr "" -#: netbox/templates/core/job.html:77 +#: templates/core/job.html:77 #, python-format msgid "every %(interval)s minutes" msgstr "" -#: netbox/templates/core/objectchange.html:29 -#: netbox/templates/users/objectpermission.html:42 +#: templates/core/objectchange.html:29 templates/users/objectpermission.html:42 msgid "Change" msgstr "" -#: netbox/templates/core/objectchange.html:79 +#: templates/core/objectchange.html:79 msgid "Difference" msgstr "" -#: netbox/templates/core/objectchange.html:82 +#: templates/core/objectchange.html:82 msgid "Previous" msgstr "" -#: netbox/templates/core/objectchange.html:85 +#: templates/core/objectchange.html:85 msgid "Next" msgstr "" -#: netbox/templates/core/objectchange.html:93 +#: templates/core/objectchange.html:93 msgid "Object Created" msgstr "" -#: netbox/templates/core/objectchange.html:95 +#: templates/core/objectchange.html:95 msgid "Object Deleted" msgstr "" -#: netbox/templates/core/objectchange.html:97 +#: templates/core/objectchange.html:97 msgid "No Changes" msgstr "" -#: netbox/templates/core/objectchange.html:111 +#: templates/core/objectchange.html:111 msgid "Pre-Change Data" msgstr "" -#: netbox/templates/core/objectchange.html:122 +#: templates/core/objectchange.html:122 msgid "Warning: Comparing non-atomic change to previous change record" msgstr "" -#: netbox/templates/core/objectchange.html:131 +#: templates/core/objectchange.html:131 msgid "Post-Change Data" msgstr "" -#: netbox/templates/core/objectchange.html:162 +#: templates/core/objectchange.html:162 #, python-format msgid "See All %(count)s Changes" msgstr "" -#: netbox/templates/core/objectchange_list.html:9 -#: netbox/templates/extras/object_changelog.html:15 +#: templates/core/objectchange_list.html:9 +#: templates/extras/object_changelog.html:15 msgid "Change log retention" msgstr "" -#: netbox/templates/core/objectchange_list.html:9 -#: netbox/templates/extras/object_changelog.html:15 +#: templates/core/objectchange_list.html:9 +#: templates/extras/object_changelog.html:15 msgid "days" msgstr "" -#: netbox/templates/core/objectchange_list.html:9 -#: netbox/templates/extras/object_changelog.html:15 +#: templates/core/objectchange_list.html:9 +#: templates/extras/object_changelog.html:15 msgid "Indefinite" msgstr "" -#: netbox/templates/core/plugin.html:22 +#: templates/core/plugin.html:22 msgid "Not installed" msgstr "" -#: netbox/templates/core/plugin.html:33 +#: templates/core/plugin.html:33 msgid "Overview" msgstr "" -#: netbox/templates/core/plugin.html:39 +#: templates/core/plugin.html:39 msgid "Install" msgstr "" -#: netbox/templates/core/plugin.html:51 +#: templates/core/plugin.html:51 msgid "Plugin Details" msgstr "" -#: netbox/templates/core/plugin.html:58 +#: templates/core/plugin.html:58 msgid "Summary" msgstr "" -#: netbox/templates/core/plugin.html:76 +#: templates/core/plugin.html:76 msgid "License" msgstr "" -#: netbox/templates/core/plugin.html:96 +#: templates/core/plugin.html:96 msgid "Version History" msgstr "" -#: netbox/templates/core/plugin.html:107 +#: templates/core/plugin.html:107 msgid "Local Installation Instructions" msgstr "" -#: netbox/templates/core/rq_queue_list.html:5 -#: netbox/templates/core/rq_queue_list.html:13 -#: netbox/templates/core/rq_task_list.html:14 -#: netbox/templates/core/rq_worker.html:7 +#: templates/core/rq_queue_list.html:5 templates/core/rq_queue_list.html:13 +#: templates/core/rq_task_list.html:14 templates/core/rq_worker.html:7 msgid "Background Queues" msgstr "" -#: netbox/templates/core/rq_queue_list.html:24 -#: netbox/templates/core/rq_queue_list.html:25 -#: netbox/templates/core/rq_worker_list.html:49 -#: netbox/templates/core/rq_worker_list.html:50 -#: netbox/templates/extras/script_result.html:67 -#: netbox/templates/extras/script_result.html:69 -#: netbox/templates/inc/table_controls_htmx.html:30 -#: netbox/templates/inc/table_controls_htmx.html:33 +#: templates/core/rq_queue_list.html:24 templates/core/rq_queue_list.html:25 +#: templates/core/rq_worker_list.html:49 templates/core/rq_worker_list.html:50 +#: templates/extras/script_result.html:67 +#: templates/extras/script_result.html:69 +#: templates/inc/table_controls_htmx.html:30 +#: templates/inc/table_controls_htmx.html:31 msgid "Configure Table" msgstr "" -#: netbox/templates/core/rq_task.html:29 +#: templates/core/rq_task.html:29 msgid "Stop" msgstr "" -#: netbox/templates/core/rq_task.html:34 +#: templates/core/rq_task.html:34 msgid "Requeue" msgstr "" -#: netbox/templates/core/rq_task.html:39 +#: templates/core/rq_task.html:39 msgid "Enqueue" msgstr "" -#: netbox/templates/core/rq_task.html:61 +#: templates/core/rq_task.html:61 msgid "Queue" msgstr "" -#: netbox/templates/core/rq_task.html:65 +#: templates/core/rq_task.html:65 msgid "Timeout" msgstr "" -#: netbox/templates/core/rq_task.html:69 +#: templates/core/rq_task.html:69 msgid "Result TTL" msgstr "" -#: netbox/templates/core/rq_task.html:89 +#: templates/core/rq_task.html:89 msgid "Meta" msgstr "" -#: netbox/templates/core/rq_task.html:93 +#: templates/core/rq_task.html:93 msgid "Arguments" msgstr "" -#: netbox/templates/core/rq_task.html:97 +#: templates/core/rq_task.html:97 msgid "Keyword Arguments" msgstr "" -#: netbox/templates/core/rq_task.html:103 +#: templates/core/rq_task.html:103 msgid "Depends on" msgstr "" -#: netbox/templates/core/rq_task.html:109 +#: templates/core/rq_task.html:109 msgid "Exception" msgstr "" -#: netbox/templates/core/rq_task_list.html:28 +#: templates/core/rq_task_list.html:28 msgid "tasks in " msgstr "" -#: netbox/templates/core/rq_task_list.html:33 +#: templates/core/rq_task_list.html:33 msgid "Queued Jobs" msgstr "" -#: netbox/templates/core/rq_task_list.html:64 -#: netbox/templates/extras/script_result.html:86 +#: templates/core/rq_task_list.html:64 templates/extras/script_result.html:86 #, python-format msgid "" "Select all %(count)s %(object_type_plural)s matching query" msgstr "" -#: netbox/templates/core/rq_worker.html:10 +#: templates/core/rq_worker.html:10 msgid "Worker Info" msgstr "" -#: netbox/templates/core/rq_worker.html:31 -#: netbox/templates/core/rq_worker.html:40 +#: templates/core/rq_worker.html:31 templates/core/rq_worker.html:40 msgid "Worker" msgstr "" -#: netbox/templates/core/rq_worker.html:55 +#: templates/core/rq_worker.html:55 msgid "Queues" msgstr "" -#: netbox/templates/core/rq_worker.html:63 +#: templates/core/rq_worker.html:63 msgid "Curent Job" msgstr "" -#: netbox/templates/core/rq_worker.html:67 +#: templates/core/rq_worker.html:67 msgid "Successful job count" msgstr "" -#: netbox/templates/core/rq_worker.html:71 +#: templates/core/rq_worker.html:71 msgid "Failed job count" msgstr "" -#: netbox/templates/core/rq_worker.html:75 +#: templates/core/rq_worker.html:75 msgid "Total working time" msgstr "" -#: netbox/templates/core/rq_worker.html:76 +#: templates/core/rq_worker.html:76 msgid "seconds" msgstr "" -#: netbox/templates/core/rq_worker_list.html:13 -#: netbox/templates/core/rq_worker_list.html:21 +#: templates/core/rq_worker_list.html:13 templates/core/rq_worker_list.html:21 msgid "Background Workers" msgstr "" -#: netbox/templates/core/rq_worker_list.html:29 +#: templates/core/rq_worker_list.html:29 #, python-format msgid "Workers in %(queue_name)s" msgstr "" -#: netbox/templates/core/system.html:11 -#: netbox/utilities/templates/buttons/export.html:4 +#: templates/core/system.html:11 utilities/templates/buttons/export.html:4 msgid "Export" msgstr "" -#: netbox/templates/core/system.html:28 +#: templates/core/system.html:28 msgid "System Status" msgstr "" -#: netbox/templates/core/system.html:31 +#: templates/core/system.html:31 msgid "NetBox release" msgstr "" -#: netbox/templates/core/system.html:44 +#: templates/core/system.html:44 msgid "Django version" msgstr "" -#: netbox/templates/core/system.html:48 +#: templates/core/system.html:48 msgid "PostgreSQL version" msgstr "" -#: netbox/templates/core/system.html:52 +#: templates/core/system.html:52 msgid "Database name" msgstr "" -#: netbox/templates/core/system.html:56 +#: templates/core/system.html:56 msgid "Database size" msgstr "" -#: netbox/templates/core/system.html:61 +#: templates/core/system.html:61 msgid "Unavailable" msgstr "" -#: netbox/templates/core/system.html:66 +#: templates/core/system.html:66 msgid "RQ workers" msgstr "" -#: netbox/templates/core/system.html:69 +#: templates/core/system.html:69 msgid "default queue" msgstr "" -#: netbox/templates/core/system.html:73 +#: templates/core/system.html:73 msgid "System time" msgstr "" -#: netbox/templates/core/system.html:85 +#: templates/core/system.html:85 msgid "Current Configuration" msgstr "" -#: netbox/templates/dcim/bulk_disconnect.html:9 +#: templates/dcim/bulk_disconnect.html:9 #, python-format msgid "" "Are you sure you want to disconnect these %(count)s %(obj_type_plural)s?" msgstr "" -#: netbox/templates/dcim/cable_trace.html:10 +#: templates/dcim/cable_trace.html:10 #, python-format msgid "Cable Trace for %(object_type)s %(object)s" msgstr "" -#: netbox/templates/dcim/cable_trace.html:24 -#: netbox/templates/dcim/inc/rack_elevation.html:7 +#: templates/dcim/cable_trace.html:24 templates/dcim/inc/rack_elevation.html:7 msgid "Download SVG" msgstr "" -#: netbox/templates/dcim/cable_trace.html:30 +#: templates/dcim/cable_trace.html:30 msgid "Asymmetric Path" msgstr "" -#: netbox/templates/dcim/cable_trace.html:31 +#: templates/dcim/cable_trace.html:31 msgid "The nodes below have no links and result in an asymmetric path" msgstr "" -#: netbox/templates/dcim/cable_trace.html:38 +#: templates/dcim/cable_trace.html:38 msgid "Path split" msgstr "" -#: netbox/templates/dcim/cable_trace.html:39 +#: templates/dcim/cable_trace.html:39 msgid "Select a node below to continue" msgstr "" -#: netbox/templates/dcim/cable_trace.html:55 +#: templates/dcim/cable_trace.html:55 msgid "Trace Completed" msgstr "" -#: netbox/templates/dcim/cable_trace.html:58 +#: templates/dcim/cable_trace.html:58 msgid "Total segments" msgstr "" -#: netbox/templates/dcim/cable_trace.html:62 +#: templates/dcim/cable_trace.html:62 msgid "Total length" msgstr "" -#: netbox/templates/dcim/cable_trace.html:77 +#: templates/dcim/cable_trace.html:77 msgid "No paths found" msgstr "" -#: netbox/templates/dcim/cable_trace.html:85 +#: templates/dcim/cable_trace.html:85 msgid "Related Paths" msgstr "" -#: netbox/templates/dcim/cable_trace.html:89 +#: templates/dcim/cable_trace.html:89 msgid "Origin" msgstr "" -#: netbox/templates/dcim/cable_trace.html:90 +#: templates/dcim/cable_trace.html:90 msgid "Destination" msgstr "" -#: netbox/templates/dcim/cable_trace.html:91 +#: templates/dcim/cable_trace.html:91 msgid "Segments" msgstr "" -#: netbox/templates/dcim/cable_trace.html:104 +#: templates/dcim/cable_trace.html:104 msgid "Incomplete" msgstr "" -#: netbox/templates/dcim/component_list.html:14 +#: templates/dcim/component_list.html:14 msgid "Rename Selected" msgstr "" -#: netbox/templates/dcim/consoleport.html:65 -#: netbox/templates/dcim/consoleserverport.html:66 -#: netbox/templates/dcim/frontport.html:98 -#: netbox/templates/dcim/interface.html:233 -#: netbox/templates/dcim/poweroutlet.html:79 -#: netbox/templates/dcim/powerport.html:69 +#: templates/dcim/consoleport.html:65 templates/dcim/consoleserverport.html:66 +#: templates/dcim/frontport.html:98 templates/dcim/interface.html:233 +#: templates/dcim/poweroutlet.html:83 templates/dcim/powerport.html:69 msgid "Not Connected" msgstr "" -#: netbox/templates/dcim/device.html:34 +#: templates/dcim/device.html:34 msgid "Highlight device in rack" msgstr "" -#: netbox/templates/dcim/device.html:55 +#: templates/dcim/device.html:55 msgid "Not racked" msgstr "" -#: netbox/templates/dcim/device.html:62 netbox/templates/dcim/site.html:94 +#: templates/dcim/device.html:62 templates/dcim/site.html:94 msgid "GPS Coordinates" msgstr "" -#: netbox/templates/dcim/device.html:68 netbox/templates/dcim/site.html:81 -#: netbox/templates/dcim/site.html:100 +#: templates/dcim/device.html:68 templates/dcim/site.html:81 +#: templates/dcim/site.html:100 msgid "Map" msgstr "" -#: netbox/templates/dcim/device.html:108 -#: netbox/templates/dcim/inventoryitem.html:60 -#: netbox/templates/dcim/module.html:81 netbox/templates/dcim/modulebay.html:74 -#: netbox/templates/dcim/rack.html:61 +#: templates/dcim/device.html:108 templates/dcim/inventoryitem.html:60 +#: templates/dcim/module.html:81 templates/dcim/modulebay.html:74 +#: templates/dcim/rack.html:61 msgid "Asset Tag" msgstr "" -#: netbox/templates/dcim/device.html:123 +#: templates/dcim/device.html:123 msgid "View Virtual Chassis" msgstr "" -#: netbox/templates/dcim/device.html:164 +#: templates/dcim/device.html:164 msgid "Create VDC" msgstr "" -#: netbox/templates/dcim/device.html:175 -#: netbox/templates/dcim/device_edit.html:66 -#: netbox/virtualization/forms/model_forms.py:230 +#: templates/dcim/device.html:175 templates/dcim/device_edit.html:66 +#: virtualization/forms/model_forms.py:230 msgid "Management" msgstr "" -#: netbox/templates/dcim/device.html:195 netbox/templates/dcim/device.html:211 -#: netbox/templates/dcim/device.html:227 -#: netbox/templates/virtualization/virtualmachine.html:57 -#: netbox/templates/virtualization/virtualmachine.html:73 +#: templates/dcim/device.html:195 templates/dcim/device.html:211 +#: templates/dcim/device.html:227 +#: templates/virtualization/virtualmachine.html:57 +#: templates/virtualization/virtualmachine.html:73 msgid "NAT for" msgstr "" -#: netbox/templates/dcim/device.html:197 netbox/templates/dcim/device.html:213 -#: netbox/templates/dcim/device.html:229 -#: netbox/templates/virtualization/virtualmachine.html:59 -#: netbox/templates/virtualization/virtualmachine.html:75 +#: templates/dcim/device.html:197 templates/dcim/device.html:213 +#: templates/dcim/device.html:229 +#: templates/virtualization/virtualmachine.html:59 +#: templates/virtualization/virtualmachine.html:75 msgid "NAT" msgstr "" -#: netbox/templates/dcim/device.html:252 netbox/templates/dcim/rack.html:73 +#: templates/dcim/device.html:252 templates/dcim/rack.html:73 msgid "Power Utilization" msgstr "" -#: netbox/templates/dcim/device.html:256 +#: templates/dcim/device.html:256 msgid "Input" msgstr "" -#: netbox/templates/dcim/device.html:257 +#: templates/dcim/device.html:257 msgid "Outlets" msgstr "" -#: netbox/templates/dcim/device.html:258 +#: templates/dcim/device.html:258 msgid "Allocated" msgstr "" -#: netbox/templates/dcim/device.html:268 netbox/templates/dcim/device.html:270 -#: netbox/templates/dcim/device.html:286 -#: netbox/templates/dcim/powerfeed.html:67 +#: templates/dcim/device.html:268 templates/dcim/device.html:270 +#: templates/dcim/device.html:286 templates/dcim/powerfeed.html:67 msgid "VA" msgstr "" -#: netbox/templates/dcim/device.html:280 +#: templates/dcim/device.html:280 msgctxt "Leg of a power feed" msgid "Leg" msgstr "" -#: netbox/templates/dcim/device.html:306 -#: netbox/templates/virtualization/virtualmachine.html:158 +#: templates/dcim/device.html:306 +#: templates/virtualization/virtualmachine.html:158 msgid "Add a service" msgstr "" -#: netbox/templates/dcim/device/base.html:21 -#: netbox/templates/dcim/device_list.html:9 -#: netbox/templates/dcim/devicetype/base.html:18 -#: netbox/templates/dcim/inc/moduletype_buttons.html:9 -#: netbox/templates/dcim/module.html:18 -#: netbox/templates/virtualization/virtualmachine/base.html:22 -#: netbox/templates/virtualization/virtualmachine_list.html:8 +#: templates/dcim/device/base.html:21 templates/dcim/device_list.html:9 +#: templates/dcim/devicetype/base.html:18 +#: templates/dcim/inc/moduletype_buttons.html:9 templates/dcim/module.html:18 +#: templates/virtualization/virtualmachine/base.html:22 +#: templates/virtualization/virtualmachine_list.html:8 msgid "Add Components" msgstr "" -#: netbox/templates/dcim/device/consoleports.html:24 +#: templates/dcim/device/consoleports.html:24 msgid "Add Console Ports" msgstr "" -#: netbox/templates/dcim/device/consoleserverports.html:24 +#: templates/dcim/device/consoleserverports.html:24 msgid "Add Console Server Ports" msgstr "" -#: netbox/templates/dcim/device/devicebays.html:10 +#: templates/dcim/device/devicebays.html:10 msgid "Add Device Bays" msgstr "" -#: netbox/templates/dcim/device/frontports.html:24 +#: templates/dcim/device/frontports.html:24 msgid "Add Front Ports" msgstr "" -#: netbox/templates/dcim/device/inc/interface_table_controls.html:9 +#: templates/dcim/device/inc/interface_table_controls.html:9 msgid "Hide Enabled" msgstr "" -#: netbox/templates/dcim/device/inc/interface_table_controls.html:10 +#: templates/dcim/device/inc/interface_table_controls.html:10 msgid "Hide Disabled" msgstr "" -#: netbox/templates/dcim/device/inc/interface_table_controls.html:11 +#: templates/dcim/device/inc/interface_table_controls.html:11 msgid "Hide Virtual" msgstr "" -#: netbox/templates/dcim/device/inc/interface_table_controls.html:12 +#: templates/dcim/device/inc/interface_table_controls.html:12 msgid "Hide Disconnected" msgstr "" -#: netbox/templates/dcim/device/interfaces.html:27 +#: templates/dcim/device/interfaces.html:27 msgid "Add Interfaces" msgstr "" -#: netbox/templates/dcim/device/inventory.html:10 -#: netbox/templates/dcim/inc/panels/inventory_items.html:10 +#: templates/dcim/device/inventory.html:10 +#: templates/dcim/inc/panels/inventory_items.html:10 msgid "Add Inventory Item" msgstr "" -#: netbox/templates/dcim/device/modulebays.html:10 +#: templates/dcim/device/modulebays.html:10 msgid "Add Module Bays" msgstr "" -#: netbox/templates/dcim/device/poweroutlets.html:24 +#: templates/dcim/device/poweroutlets.html:24 msgid "Add Power Outlets" msgstr "" -#: netbox/templates/dcim/device/powerports.html:24 +#: templates/dcim/device/powerports.html:24 msgid "Add Power Port" msgstr "" -#: netbox/templates/dcim/device/rearports.html:24 +#: templates/dcim/device/rearports.html:24 msgid "Add Rear Ports" msgstr "" -#: netbox/templates/dcim/device_edit.html:46 +#: templates/dcim/device_edit.html:46 msgid "Parent Bay" msgstr "" -#: netbox/templates/dcim/device_edit.html:50 -#: netbox/utilities/templates/form_helpers/render_field.html:22 +#: templates/dcim/device_edit.html:50 +#: utilities/templates/form_helpers/render_field.html:22 msgid "Regenerate Slug" msgstr "" -#: netbox/templates/dcim/device_edit.html:51 -#: netbox/templates/generic/bulk_remove.html:21 -#: netbox/utilities/templates/helpers/table_config_form.html:23 +#: templates/dcim/device_edit.html:51 templates/extras/tableconfig_edit.html:32 +#: templates/generic/bulk_remove.html:21 +#: utilities/templates/helpers/table_config_form.html:23 msgid "Remove" msgstr "" -#: netbox/templates/dcim/device_edit.html:112 +#: templates/dcim/device_edit.html:112 msgid "Local Config Context Data" msgstr "" -#: netbox/templates/dcim/device_list.html:82 -#: netbox/templates/generic/bulk_rename.html:57 -#: netbox/templates/virtualization/virtualmachine/interfaces.html:11 -#: netbox/templates/virtualization/virtualmachine/virtual_disks.html:11 +#: templates/dcim/device_list.html:82 templates/generic/bulk_rename.html:57 +#: templates/virtualization/virtualmachine/interfaces.html:11 +#: templates/virtualization/virtualmachine/virtual_disks.html:11 msgid "Rename" msgstr "" -#: netbox/templates/dcim/devicebay.html:17 +#: templates/dcim/devicebay.html:17 msgid "Device Bay" msgstr "" -#: netbox/templates/dcim/devicebay.html:43 +#: templates/dcim/devicebay.html:43 msgid "Installed Device" msgstr "" -#: netbox/templates/dcim/devicebay_depopulate.html:6 +#: templates/dcim/devicebay_depopulate.html:6 #, python-format msgid "Remove %(device)s from %(device_bay)s?" msgstr "" -#: netbox/templates/dcim/devicebay_depopulate.html:13 +#: templates/dcim/devicebay_depopulate.html:13 #, python-format msgid "" "Are you sure you want to remove %(device)s from " "%(device_bay)s?" msgstr "" -#: netbox/templates/dcim/devicebay_populate.html:13 +#: templates/dcim/devicebay_populate.html:13 msgid "Populate" msgstr "" -#: netbox/templates/dcim/devicebay_populate.html:22 +#: templates/dcim/devicebay_populate.html:22 msgid "Bay" msgstr "" -#: netbox/templates/dcim/devicerole.html:14 -#: netbox/templates/dcim/platform.html:17 +#: templates/dcim/devicerole.html:14 templates/dcim/platform.html:17 msgid "Add Device" msgstr "" -#: netbox/templates/dcim/devicerole.html:40 +#: templates/dcim/devicerole.html:44 msgid "VM Role" msgstr "" -#: netbox/templates/dcim/devicetype.html:18 -#: netbox/templates/dcim/moduletype.html:31 +#: templates/dcim/devicerole.html:67 +msgid "Child Device Roles" +msgstr "" + +#: templates/dcim/devicerole.html:71 +msgid "Add a Device Role" +msgstr "" + +#: templates/dcim/devicetype.html:18 templates/dcim/moduletype.html:35 msgid "Model Name" msgstr "" -#: netbox/templates/dcim/devicetype.html:25 -#: netbox/templates/dcim/moduletype.html:35 +#: templates/dcim/devicetype.html:25 templates/dcim/moduletype.html:39 msgid "Part Number" msgstr "" -#: netbox/templates/dcim/devicetype.html:41 +#: templates/dcim/devicetype.html:41 msgid "Exclude From Utilization" msgstr "" -#: netbox/templates/dcim/devicetype.html:59 +#: templates/dcim/devicetype.html:59 msgid "Parent/Child" msgstr "" -#: netbox/templates/dcim/devicetype.html:71 +#: templates/dcim/devicetype.html:71 msgid "Front Image" msgstr "" -#: netbox/templates/dcim/devicetype.html:83 +#: templates/dcim/devicetype.html:83 msgid "Rear Image" msgstr "" -#: netbox/templates/dcim/frontport.html:54 +#: templates/dcim/frontport.html:54 msgid "Rear Port Position" msgstr "" -#: netbox/templates/dcim/frontport.html:72 -#: netbox/templates/dcim/interface.html:201 -#: netbox/templates/dcim/poweroutlet.html:73 -#: netbox/templates/dcim/powerport.html:63 -#: netbox/templates/dcim/rearport.html:68 +#: templates/dcim/frontport.html:72 templates/dcim/interface.html:201 +#: templates/dcim/poweroutlet.html:77 templates/dcim/powerport.html:63 +#: templates/dcim/rearport.html:68 msgid "Marked as Connected" msgstr "" -#: netbox/templates/dcim/frontport.html:86 -#: netbox/templates/dcim/rearport.html:82 +#: templates/dcim/frontport.html:86 templates/dcim/rearport.html:82 msgid "Connection Status" msgstr "" -#: netbox/templates/dcim/htmx/cable_edit.html:13 +#: templates/dcim/htmx/cable_edit.html:13 msgid "A Side" msgstr "" -#: netbox/templates/dcim/htmx/cable_edit.html:33 +#: templates/dcim/htmx/cable_edit.html:33 msgid "B Side" msgstr "" -#: netbox/templates/dcim/inc/cable_termination.html:82 +#: templates/dcim/inc/cable_termination.html:82 msgid "No termination" msgstr "" -#: netbox/templates/dcim/inc/cable_toggle_buttons.html:3 +#: templates/dcim/inc/cable_toggle_buttons.html:3 msgid "Mark Planned" msgstr "" -#: netbox/templates/dcim/inc/cable_toggle_buttons.html:6 +#: templates/dcim/inc/cable_toggle_buttons.html:6 msgid "Mark Installed" msgstr "" -#: netbox/templates/dcim/inc/connection_endpoints.html:13 +#: templates/dcim/inc/connection_endpoints.html:13 msgid "Path Status" msgstr "" -#: netbox/templates/dcim/inc/connection_endpoints.html:18 +#: templates/dcim/inc/connection_endpoints.html:18 msgid "Not Reachable" msgstr "" -#: netbox/templates/dcim/inc/connection_endpoints.html:23 +#: templates/dcim/inc/connection_endpoints.html:23 msgid "Path Endpoints" msgstr "" -#: netbox/templates/dcim/inc/endpoint_connection.html:8 -#: netbox/templates/dcim/powerfeed.html:120 -#: netbox/templates/dcim/rearport.html:94 +#: templates/dcim/inc/endpoint_connection.html:8 +#: templates/dcim/powerfeed.html:120 templates/dcim/rearport.html:94 msgid "Not connected" msgstr "" -#: netbox/templates/dcim/inc/interface_vlans_table.html:6 +#: templates/dcim/inc/interface_vlans_table.html:6 msgid "Untagged" msgstr "" -#: netbox/templates/dcim/inc/interface_vlans_table.html:37 +#: templates/dcim/inc/interface_vlans_table.html:37 msgid "No VLANs Assigned" msgstr "" -#: netbox/templates/dcim/inc/interface_vlans_table.html:44 -#: netbox/templates/ipam/prefix_list.html:16 -#: netbox/templates/ipam/prefix_list.html:33 +#: templates/dcim/inc/interface_vlans_table.html:44 +#: templates/ipam/prefix_list.html:16 templates/ipam/prefix_list.html:33 msgid "Clear" msgstr "" -#: netbox/templates/dcim/inc/interface_vlans_table.html:47 +#: templates/dcim/inc/interface_vlans_table.html:47 msgid "Clear All" msgstr "" -#: netbox/templates/dcim/inc/panels/racktype_dimensions.html:38 +#: templates/dcim/inc/panels/racktype_dimensions.html:48 msgid "Mounting Depth" msgstr "" -#: netbox/templates/dcim/inc/panels/racktype_numbering.html:6 +#: templates/dcim/inc/panels/racktype_numbering.html:6 msgid "Starting Unit" msgstr "" -#: netbox/templates/dcim/inc/panels/racktype_numbering.html:10 +#: templates/dcim/inc/panels/racktype_numbering.html:10 msgid "Descending Units" msgstr "" -#: netbox/templates/dcim/inc/rack_elevation.html:3 +#: templates/dcim/inc/rack_elevation.html:3 msgid "Rack elevation" msgstr "" -#: netbox/templates/dcim/interface.html:17 +#: templates/dcim/interface.html:17 msgid "Add Child Interface" msgstr "" -#: netbox/templates/dcim/interface.html:50 +#: templates/dcim/interface.html:50 msgid "Speed/Duplex" msgstr "" -#: netbox/templates/dcim/interface.html:73 +#: templates/dcim/interface.html:73 msgid "PoE Mode" msgstr "" -#: netbox/templates/dcim/interface.html:77 +#: templates/dcim/interface.html:77 msgid "PoE Type" msgstr "" -#: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:94 +#: templates/dcim/interface.html:156 +#: templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "" -#: netbox/templates/dcim/interface.html:208 +#: templates/dcim/interface.html:208 msgid "Wireless Link" msgstr "" -#: netbox/templates/dcim/interface.html:287 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:26 +#: templates/dcim/interface.html:287 +#: templates/wireless/inc/wirelesslink_interface.html:26 msgid "Channel" msgstr "" -#: netbox/templates/dcim/interface.html:296 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:32 +#: templates/dcim/interface.html:296 +#: templates/wireless/inc/wirelesslink_interface.html:32 msgid "Channel Frequency" msgstr "" -#: netbox/templates/dcim/interface.html:299 -#: netbox/templates/dcim/interface.html:307 -#: netbox/templates/dcim/interface.html:318 -#: netbox/templates/dcim/interface.html:326 +#: templates/dcim/interface.html:299 templates/dcim/interface.html:307 +#: templates/dcim/interface.html:318 templates/dcim/interface.html:326 msgid "MHz" msgstr "" -#: netbox/templates/dcim/interface.html:315 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:42 +#: templates/dcim/interface.html:315 +#: templates/wireless/inc/wirelesslink_interface.html:42 msgid "Channel Width" msgstr "" -#: netbox/templates/dcim/interface.html:342 -#: netbox/templates/wireless/wirelesslan.html:14 -#: netbox/templates/wireless/wirelesslink.html:21 -#: netbox/wireless/forms/bulk_edit.py:62 netbox/wireless/forms/bulk_edit.py:105 -#: netbox/wireless/forms/filtersets.py:43 -#: netbox/wireless/forms/filtersets.py:108 netbox/wireless/models.py:82 -#: netbox/wireless/models.py:153 netbox/wireless/tables/wirelesslan.py:44 +#: templates/dcim/interface.html:342 templates/wireless/wirelesslan.html:14 +#: templates/wireless/wirelesslink.html:21 wireless/forms/bulk_edit.py:63 +#: wireless/forms/bulk_edit.py:106 wireless/forms/filtersets.py:43 +#: wireless/forms/filtersets.py:108 wireless/models.py:82 +#: wireless/models.py:145 wireless/tables/wirelesslan.py:44 msgid "SSID" msgstr "" -#: netbox/templates/dcim/interface.html:362 +#: templates/dcim/interface.html:362 msgid "LAG Members" msgstr "" -#: netbox/templates/dcim/interface.html:380 +#: templates/dcim/interface.html:380 msgid "No member interfaces" msgstr "" -#: netbox/templates/dcim/interface.html:400 -#: netbox/templates/ipam/fhrpgroup.html:73 -#: netbox/templates/ipam/iprange/ip_addresses.html:7 -#: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:111 +#: templates/dcim/interface.html:400 templates/ipam/fhrpgroup.html:74 +#: templates/ipam/iprange/ip_addresses.html:7 +#: templates/ipam/prefix/ip_addresses.html:7 +#: templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "" -#: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:129 +#: templates/dcim/interface.html:417 +#: templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "" -#: netbox/templates/dcim/inventoryitem.html:24 +#: templates/dcim/inventoryitem.html:24 msgid "Parent Item" msgstr "" -#: netbox/templates/dcim/inventoryitem.html:52 +#: templates/dcim/inventoryitem.html:52 msgid "Part ID" msgstr "" -#: netbox/templates/dcim/location.html:17 +#: templates/dcim/location.html:17 msgid "Add Child Location" msgstr "" -#: netbox/templates/dcim/location.html:77 +#: templates/dcim/location.html:78 msgid "Child Locations" msgstr "" -#: netbox/templates/dcim/location.html:81 netbox/templates/dcim/site.html:131 +#: templates/dcim/location.html:82 templates/dcim/site.html:131 msgid "Add a Location" msgstr "" -#: netbox/templates/dcim/location.html:94 netbox/templates/dcim/site.html:144 +#: templates/dcim/location.html:95 templates/dcim/site.html:144 msgid "Add a Device" msgstr "" -#: netbox/templates/dcim/macaddress.html:36 +#: templates/dcim/macaddress.html:36 msgid "Primary for interface" msgstr "" -#: netbox/templates/dcim/manufacturer.html:16 +#: templates/dcim/manufacturer.html:16 msgid "Add Device Type" msgstr "" -#: netbox/templates/dcim/manufacturer.html:21 +#: templates/dcim/manufacturer.html:21 templates/dcim/moduletypeprofile.html:49 msgid "Add Module Type" msgstr "" -#: netbox/templates/dcim/powerfeed.html:53 +#: templates/dcim/moduletype.html:71 +msgid "No profile assigned" +msgstr "" + +#: templates/dcim/moduletypeprofile.html:13 +msgid "Module Type Profile" +msgstr "" + +#: templates/dcim/powerfeed.html:53 msgid "Connected Device" msgstr "" -#: netbox/templates/dcim/powerfeed.html:63 +#: templates/dcim/powerfeed.html:63 msgid "Utilization (Allocated" msgstr "" -#: netbox/templates/dcim/powerfeed.html:80 +#: templates/dcim/powerfeed.html:80 msgid "Electrical Characteristics" msgstr "" -#: netbox/templates/dcim/powerfeed.html:88 +#: templates/dcim/powerfeed.html:88 msgctxt "Abbreviation for volts" msgid "V" msgstr "" -#: netbox/templates/dcim/powerfeed.html:92 +#: templates/dcim/powerfeed.html:92 msgctxt "Abbreviation for amperes" msgid "A" msgstr "" -#: netbox/templates/dcim/poweroutlet.html:58 +#: templates/dcim/poweroutlet.html:62 msgid "Feed Leg" msgstr "" -#: netbox/templates/dcim/powerpanel.html:72 +#: templates/dcim/powerpanel.html:72 msgid "Add Power Feeds" msgstr "" -#: netbox/templates/dcim/powerport.html:44 +#: templates/dcim/powerport.html:44 msgid "Maximum Draw" msgstr "" -#: netbox/templates/dcim/powerport.html:48 +#: templates/dcim/powerport.html:48 msgid "Allocated Draw" msgstr "" -#: netbox/templates/dcim/rack.html:69 +#: templates/dcim/rack.html:69 msgid "Space Utilization" msgstr "" -#: netbox/templates/dcim/rack.html:84 netbox/templates/dcim/racktype.html:44 +#: templates/dcim/rack.html:84 templates/dcim/racktype.html:44 msgid "Rack Weight" msgstr "" -#: netbox/templates/dcim/rack.html:94 netbox/templates/dcim/racktype.html:54 +#: templates/dcim/rack.html:94 templates/dcim/racktype.html:54 msgid "Maximum Weight" msgstr "" -#: netbox/templates/dcim/rack.html:104 +#: templates/dcim/rack.html:104 msgid "Total Weight" msgstr "" -#: netbox/templates/dcim/rack.html:125 -#: netbox/templates/dcim/rack_elevation_list.html:15 +#: templates/dcim/rack.html:125 templates/dcim/rack_elevation_list.html:15 msgid "Images and Labels" msgstr "" -#: netbox/templates/dcim/rack.html:126 -#: netbox/templates/dcim/rack_elevation_list.html:16 +#: templates/dcim/rack.html:126 templates/dcim/rack_elevation_list.html:16 msgid "Images only" msgstr "" -#: netbox/templates/dcim/rack.html:127 -#: netbox/templates/dcim/rack_elevation_list.html:17 +#: templates/dcim/rack.html:127 templates/dcim/rack_elevation_list.html:17 msgid "Labels only" msgstr "" -#: netbox/templates/dcim/rack/reservations.html:8 +#: templates/dcim/rack/reservations.html:8 msgid "Add reservation" msgstr "" -#: netbox/templates/dcim/rack_elevation_list.html:12 +#: templates/dcim/rack_elevation_list.html:12 msgid "View List" msgstr "" -#: netbox/templates/dcim/rack_elevation_list.html:14 +#: templates/dcim/rack_elevation_list.html:14 msgid "Select rack view" msgstr "" -#: netbox/templates/dcim/rack_elevation_list.html:25 +#: templates/dcim/rack_elevation_list.html:25 msgid "Sort By" msgstr "" -#: netbox/templates/dcim/rack_elevation_list.html:74 +#: templates/dcim/rack_elevation_list.html:74 msgid "No Racks Found" msgstr "" -#: netbox/templates/dcim/rack_list.html:8 +#: templates/dcim/rack_list.html:8 msgid "View Elevations" msgstr "" -#: netbox/templates/dcim/rackreservation.html:42 +#: templates/dcim/rackreservation.html:42 msgid "Reservation Details" msgstr "" -#: netbox/templates/dcim/rackrole.html:10 +#: templates/dcim/rackrole.html:10 msgid "Add Rack" msgstr "" -#: netbox/templates/dcim/rearport.html:50 +#: templates/dcim/rearport.html:50 msgid "Positions" msgstr "" -#: netbox/templates/dcim/region.html:17 netbox/templates/dcim/sitegroup.html:17 +#: templates/dcim/region.html:17 templates/dcim/sitegroup.html:17 msgid "Add Site" msgstr "" -#: netbox/templates/dcim/region.html:55 +#: templates/dcim/region.html:56 msgid "Child Regions" msgstr "" -#: netbox/templates/dcim/region.html:59 +#: templates/dcim/region.html:60 msgid "Add Region" msgstr "" -#: netbox/templates/dcim/site.html:64 +#: templates/dcim/site.html:64 msgid "Time Zone" msgstr "" -#: netbox/templates/dcim/site.html:67 +#: templates/dcim/site.html:67 msgid "UTC" msgstr "" -#: netbox/templates/dcim/site.html:68 +#: templates/dcim/site.html:68 msgid "Site time" msgstr "" -#: netbox/templates/dcim/site.html:75 +#: templates/dcim/site.html:75 msgid "Physical Address" msgstr "" -#: netbox/templates/dcim/site.html:90 +#: templates/dcim/site.html:90 msgid "Shipping Address" msgstr "" -#: netbox/templates/dcim/sitegroup.html:55 -#: netbox/templates/tenancy/contactgroup.html:46 -#: netbox/templates/tenancy/tenantgroup.html:55 -#: netbox/templates/wireless/wirelesslangroup.html:55 +#: templates/dcim/sitegroup.html:56 templates/tenancy/contactgroup.html:47 +#: templates/tenancy/tenantgroup.html:56 +#: templates/wireless/wirelesslangroup.html:56 msgid "Child Groups" msgstr "" -#: netbox/templates/dcim/sitegroup.html:59 +#: templates/dcim/sitegroup.html:60 msgid "Add Site Group" msgstr "" -#: netbox/templates/dcim/trace/attachment.html:5 -#: netbox/templates/extras/exporttemplate.html:31 +#: templates/dcim/trace/attachment.html:5 +#: templates/extras/configtemplate.html:33 +#: templates/extras/exporttemplate.html:40 msgid "Attachment" msgstr "" -#: netbox/templates/dcim/virtualchassis.html:57 +#: templates/dcim/virtualchassis.html:57 msgid "Add Member" msgstr "" -#: netbox/templates/dcim/virtualchassis_add.html:22 +#: templates/dcim/virtualchassis_add.html:22 msgid "Member Devices" msgstr "" -#: netbox/templates/dcim/virtualchassis_add_member.html:10 +#: templates/dcim/virtualchassis_add_member.html:10 #, python-format msgid "Add New Member to Virtual Chassis %(virtual_chassis)s" msgstr "" -#: netbox/templates/dcim/virtualchassis_add_member.html:19 +#: templates/dcim/virtualchassis_add_member.html:19 msgid "Add New Member" msgstr "" -#: netbox/templates/dcim/virtualchassis_add_member.html:27 -#: netbox/templates/generic/object_edit.html:78 -#: netbox/templates/users/objectpermission.html:31 -#: netbox/users/forms/filtersets.py:67 netbox/users/forms/model_forms.py:312 +#: templates/dcim/virtualchassis_add_member.html:27 +#: templates/generic/object_edit.html:78 +#: templates/users/objectpermission.html:31 users/forms/filtersets.py:67 +#: users/forms/model_forms.py:312 msgid "Actions" msgstr "" -#: netbox/templates/dcim/virtualchassis_add_member.html:29 +#: templates/dcim/virtualchassis_add_member.html:29 msgid "Save & Add Another" msgstr "" -#: netbox/templates/dcim/virtualchassis_edit.html:7 +#: templates/dcim/virtualchassis_edit.html:7 #, python-format msgid "Editing Virtual Chassis %(name)s" msgstr "" -#: netbox/templates/dcim/virtualchassis_edit.html:57 +#: templates/dcim/virtualchassis_edit.html:57 msgid "Rack/Unit" msgstr "" -#: netbox/templates/dcim/virtualchassis_remove_member.html:5 +#: templates/dcim/virtualchassis_edit.html:111 +#: templates/generic/bulk_add_component.html:68 +#: templates/generic/object_edit.html:47 templates/generic/object_edit.html:80 +#: templates/htmx/quick_add.html:24 +#: templates/ipam/inc/ipaddress_edit_header.html:7 +msgid "Create" +msgstr "" + +#: templates/dcim/virtualchassis_remove_member.html:5 msgid "Remove Virtual Chassis Member" msgstr "" -#: netbox/templates/dcim/virtualchassis_remove_member.html:9 +#: templates/dcim/virtualchassis_remove_member.html:9 #, python-format msgid "" "Are you sure you want to remove %(device)s from virtual " "chassis %(name)s?" msgstr "" -#: netbox/templates/dcim/virtualdevicecontext.html:26 -#: netbox/templates/vpn/l2vpn.html:18 +#: templates/dcim/virtualdevicecontext.html:26 templates/vpn/l2vpn.html:18 msgid "Identifier" msgstr "" -#: netbox/templates/exceptions/import_error.html:6 +#: templates/exceptions/import_error.html:6 msgid "" "A module import error occurred during this request. Common causes include " "the following:" msgstr "" -#: netbox/templates/exceptions/import_error.html:10 +#: templates/exceptions/import_error.html:10 msgid "Missing required packages" msgstr "" -#: netbox/templates/exceptions/import_error.html:11 +#: templates/exceptions/import_error.html:11 msgid "" "This installation of NetBox might be missing one or more required Python " "packages. These packages are listed in requirements.txt and " @@ -13499,28 +13022,28 @@ msgid "" "of required packages." msgstr "" -#: netbox/templates/exceptions/import_error.html:20 +#: templates/exceptions/import_error.html:20 msgid "WSGI service not restarted after upgrade" msgstr "" -#: netbox/templates/exceptions/import_error.html:21 +#: templates/exceptions/import_error.html:21 msgid "" "If this installation has recently been upgraded, check that the WSGI service " "(e.g. gunicorn or uWSGI) has been restarted. This ensures that the new code " "is running." msgstr "" -#: netbox/templates/exceptions/permission_error.html:6 +#: templates/exceptions/permission_error.html:6 msgid "" "A file permission error was detected while processing this request. Common " "causes include the following:" msgstr "" -#: netbox/templates/exceptions/permission_error.html:10 +#: templates/exceptions/permission_error.html:10 msgid "Insufficient write permission to the media root" msgstr "" -#: netbox/templates/exceptions/permission_error.html:11 +#: templates/exceptions/permission_error.html:11 #, python-format msgid "" "The configured media root is %(media_root)s. Ensure that the " @@ -13528,407 +13051,418 @@ msgid "" "path." msgstr "" -#: netbox/templates/exceptions/programming_error.html:6 +#: templates/exceptions/programming_error.html:6 msgid "" "A database programming error was detected while processing this request. " "Common causes include the following:" msgstr "" -#: netbox/templates/exceptions/programming_error.html:10 +#: templates/exceptions/programming_error.html:10 msgid "Database migrations missing" msgstr "" -#: netbox/templates/exceptions/programming_error.html:11 +#: templates/exceptions/programming_error.html:11 msgid "" "When upgrading to a new NetBox release, the upgrade script must be run to " "apply any new database migrations. You can run migrations manually by " "executing python3 manage.py migrate from the command line." msgstr "" -#: netbox/templates/exceptions/programming_error.html:18 +#: templates/exceptions/programming_error.html:18 msgid "Unsupported PostgreSQL version" msgstr "" -#: netbox/templates/exceptions/programming_error.html:19 +#: templates/exceptions/programming_error.html:19 msgid "" "Ensure that PostgreSQL version 12 or later is in use. You can check this by " "connecting to the database using NetBox's credentials and issuing a query " "for SELECT VERSION()." msgstr "" -#: netbox/templates/extras/configcontext.html:45 -#: netbox/templates/extras/configtemplate.html:37 -#: netbox/templates/extras/exporttemplate.html:51 +#: templates/extras/configcontext.html:45 +#: templates/extras/configtemplate.html:53 +#: templates/extras/exporttemplate.html:60 msgid "The data file associated with this object has been deleted" msgstr "" -#: netbox/templates/extras/configcontext.html:54 -#: netbox/templates/extras/configtemplate.html:46 -#: netbox/templates/extras/exporttemplate.html:60 +#: templates/extras/configcontext.html:54 +#: templates/extras/configtemplate.html:62 +#: templates/extras/exporttemplate.html:69 msgid "Data Synced" msgstr "" -#: netbox/templates/extras/configcontext_list.html:7 -#: netbox/templates/extras/configtemplate_list.html:7 -#: netbox/templates/extras/exporttemplate_list.html:7 +#: templates/extras/configcontext_list.html:7 +#: templates/extras/configtemplate_list.html:7 +#: templates/extras/exporttemplate_list.html:7 msgid "Sync Data" msgstr "" -#: netbox/templates/extras/configtemplate.html:56 +#: templates/extras/configtemplate.html:72 +#: templates/extras/exporttemplate.html:88 msgid "Environment Parameters" msgstr "" -#: netbox/templates/extras/configtemplate.html:67 -#: netbox/templates/extras/exporttemplate.html:79 +#: templates/extras/configtemplate.html:87 +#: templates/extras/exporttemplate.html:103 msgid "Template" msgstr "" -#: netbox/templates/extras/customfield.html:30 -#: netbox/templates/extras/customlink.html:21 +#: templates/extras/customfield.html:30 templates/extras/customlink.html:21 msgid "Group Name" msgstr "" -#: netbox/templates/extras/customfield.html:42 +#: templates/extras/customfield.html:42 msgid "Must be Unique" msgstr "" -#: netbox/templates/extras/customfield.html:46 +#: templates/extras/customfield.html:46 msgid "Cloneable" msgstr "" -#: netbox/templates/extras/customfield.html:56 +#: templates/extras/customfield.html:56 msgid "Default Value" msgstr "" -#: netbox/templates/extras/customfield.html:73 +#: templates/extras/customfield.html:73 msgid "Search Weight" msgstr "" -#: netbox/templates/extras/customfield.html:83 +#: templates/extras/customfield.html:83 msgid "Filter Logic" msgstr "" -#: netbox/templates/extras/customfield.html:87 +#: templates/extras/customfield.html:87 msgid "Display Weight" msgstr "" -#: netbox/templates/extras/customfield.html:91 +#: templates/extras/customfield.html:91 msgid "UI Visible" msgstr "" -#: netbox/templates/extras/customfield.html:95 +#: templates/extras/customfield.html:95 msgid "UI Editable" msgstr "" -#: netbox/templates/extras/customfield.html:115 +#: templates/extras/customfield.html:115 msgid "Validation Rules" msgstr "" -#: netbox/templates/extras/customfield.html:126 +#: templates/extras/customfield.html:126 msgid "Regular Expression" msgstr "" -#: netbox/templates/extras/customlink.html:29 +#: templates/extras/customlink.html:29 msgid "Button Class" msgstr "" -#: netbox/templates/extras/customlink.html:39 -#: netbox/templates/extras/exporttemplate.html:66 -#: netbox/templates/extras/savedfilter.html:39 +#: templates/extras/customlink.html:39 templates/extras/exporttemplate.html:78 +#: templates/extras/savedfilter.html:39 msgid "Assigned Models" msgstr "" -#: netbox/templates/extras/customlink.html:52 +#: templates/extras/customlink.html:52 msgid "Link Text" msgstr "" -#: netbox/templates/extras/customlink.html:58 +#: templates/extras/customlink.html:58 msgid "Link URL" msgstr "" -#: netbox/templates/extras/dashboard/reset.html:4 netbox/templates/home.html:66 +#: templates/extras/dashboard/reset.html:4 templates/home.html:66 msgid "Reset Dashboard" msgstr "" -#: netbox/templates/extras/dashboard/reset.html:8 +#: templates/extras/dashboard/reset.html:8 msgid "" "This will remove all configured widgets and restore the " "default dashboard configuration." msgstr "" -#: netbox/templates/extras/dashboard/reset.html:13 +#: templates/extras/dashboard/reset.html:13 msgid "" "This change affects only your dashboard, and will not impact other " "users." msgstr "" -#: netbox/templates/extras/dashboard/widget.html:21 +#: templates/extras/dashboard/widget.html:21 msgid "widget configuration" msgstr "" -#: netbox/templates/extras/dashboard/widget.html:36 +#: templates/extras/dashboard/widget.html:36 msgid "Close widget" msgstr "" -#: netbox/templates/extras/dashboard/widget_add.html:7 +#: templates/extras/dashboard/widget_add.html:7 msgid "Add a Widget" msgstr "" -#: netbox/templates/extras/dashboard/widgets/bookmarks.html:14 +#: templates/extras/dashboard/widgets/bookmarks.html:14 msgid "No bookmarks have been added yet." msgstr "" -#: netbox/templates/extras/dashboard/widgets/objectcounts.html:10 +#: templates/extras/dashboard/widgets/objectcounts.html:10 msgid "No permission" msgstr "" -#: netbox/templates/extras/dashboard/widgets/objectlist.html:6 +#: templates/extras/dashboard/widgets/objectlist.html:6 msgid "No permission to view this content" msgstr "" -#: netbox/templates/extras/dashboard/widgets/objectlist.html:10 +#: templates/extras/dashboard/widgets/objectlist.html:10 msgid "Unable to load content. Invalid view name" msgstr "" -#: netbox/templates/extras/dashboard/widgets/rssfeed.html:12 +#: templates/extras/dashboard/widgets/rssfeed.html:12 msgid "No content found" msgstr "" -#: netbox/templates/extras/dashboard/widgets/rssfeed.html:17 +#: templates/extras/dashboard/widgets/rssfeed.html:17 msgid "" "This RSS feed requires an external connection. Check the ISOLATED_DEPLOYMENT " "setting." msgstr "" -#: netbox/templates/extras/dashboard/widgets/rssfeed.html:22 +#: templates/extras/dashboard/widgets/rssfeed.html:22 msgid "There was a problem fetching the RSS feed" msgstr "" -#: netbox/templates/extras/dashboard/widgets/rssfeed.html:25 +#: templates/extras/dashboard/widgets/rssfeed.html:25 msgid "HTTP" msgstr "" -#: netbox/templates/extras/eventrule.html:61 +#: templates/extras/eventrule.html:61 msgid "Conditions" msgstr "" -#: netbox/templates/extras/exporttemplate.html:23 -msgid "MIME Type" -msgstr "" - -#: netbox/templates/extras/exporttemplate.html:27 -msgid "File Extension" -msgstr "" - -#: netbox/templates/extras/htmx/script_result.html:10 +#: templates/extras/htmx/script_result.html:10 msgid "Scheduled for" msgstr "" -#: netbox/templates/extras/htmx/script_result.html:15 +#: templates/extras/htmx/script_result.html:15 msgid "Duration" msgstr "" -#: netbox/templates/extras/htmx/script_result.html:23 +#: templates/extras/htmx/script_result.html:23 msgid "Test Summary" msgstr "" -#: netbox/templates/extras/htmx/script_result.html:43 +#: templates/extras/htmx/script_result.html:43 msgid "Log" msgstr "" -#: netbox/templates/extras/htmx/script_result.html:56 +#: templates/extras/htmx/script_result.html:56 msgid "Output" msgstr "" -#: netbox/templates/extras/inc/result_pending.html:4 +#: templates/extras/inc/result_pending.html:4 msgid "Loading" msgstr "" -#: netbox/templates/extras/inc/result_pending.html:6 +#: templates/extras/inc/result_pending.html:6 msgid "Results pending" msgstr "" -#: netbox/templates/extras/journalentry.html:15 +#: templates/extras/journalentry.html:15 msgid "Journal Entry" msgstr "" -#: netbox/templates/extras/notificationgroup.html:11 +#: templates/extras/notificationgroup.html:11 msgid "Notification Group" msgstr "" -#: netbox/templates/extras/notificationgroup.html:36 -#: netbox/templates/extras/notificationgroup.html:46 -#: netbox/utilities/templates/widgets/clearable_file_input.html:12 +#: templates/extras/notificationgroup.html:36 +#: templates/extras/notificationgroup.html:46 +#: utilities/templates/widgets/clearable_file_input.html:12 msgid "None assigned" msgstr "" -#: netbox/templates/extras/object_configcontext.html:19 +#: templates/extras/object_configcontext.html:19 msgid "The local config context overwrites all source contexts" msgstr "" -#: netbox/templates/extras/object_configcontext.html:25 +#: templates/extras/object_configcontext.html:25 msgid "Source Contexts" msgstr "" -#: netbox/templates/extras/object_journal.html:17 +#: templates/extras/object_journal.html:17 msgid "New Journal Entry" msgstr "" -#: netbox/templates/extras/object_render_config.html:6 +#: templates/extras/object_render_config.html:6 msgid "Config" msgstr "" -#: netbox/templates/extras/object_render_config.html:36 +#: templates/extras/object_render_config.html:36 msgid "Context Data" msgstr "" -#: netbox/templates/extras/object_render_config.html:56 +#: templates/extras/object_render_config.html:56 msgid "Rendered Config" msgstr "" -#: netbox/templates/extras/object_render_config.html:60 +#: templates/extras/object_render_config.html:60 msgid "Download" msgstr "" -#: netbox/templates/extras/object_render_config.html:68 +#: templates/extras/object_render_config.html:68 msgid "Error rendering template" msgstr "" -#: netbox/templates/extras/object_render_config.html:74 +#: templates/extras/object_render_config.html:74 msgid "No configuration template has been assigned." msgstr "" -#: netbox/templates/extras/report/base.html:30 +#: templates/extras/report/base.html:30 msgid "Report" msgstr "" -#: netbox/templates/extras/script.html:14 +#: templates/extras/script.html:14 msgid "You do not have permission to run scripts" msgstr "" -#: netbox/templates/extras/script.html:41 -#: netbox/templates/extras/script.html:45 -#: netbox/templates/extras/script_list.html:90 +#: templates/extras/script.html:41 templates/extras/script.html:45 +#: templates/extras/script_list.html:90 msgid "Run Script" msgstr "" -#: netbox/templates/extras/script.html:51 -#: netbox/templates/extras/script/source.html:10 +#: templates/extras/script.html:51 templates/extras/script/source.html:10 msgid "Error loading script" msgstr "" -#: netbox/templates/extras/script/jobs.html:16 +#: templates/extras/script/jobs.html:16 msgid "Script no longer exists in the source file." msgstr "" -#: netbox/templates/extras/script_list.html:47 +#: templates/extras/script_list.html:47 msgid "Last Run" msgstr "" -#: netbox/templates/extras/script_list.html:62 +#: templates/extras/script_list.html:62 msgid "Script is no longer present in the source file" msgstr "" -#: netbox/templates/extras/script_list.html:75 +#: templates/extras/script_list.html:75 msgid "Never" msgstr "" -#: netbox/templates/extras/script_list.html:88 +#: templates/extras/script_list.html:88 msgid "Run Again" msgstr "" -#: netbox/templates/extras/script_list.html:136 +#: templates/extras/script_list.html:136 #, python-format msgid "Could not load scripts from module %(module)s" msgstr "" -#: netbox/templates/extras/script_list.html:144 +#: templates/extras/script_list.html:144 msgid "No Scripts Found" msgstr "" -#: netbox/templates/extras/script_list.html:147 +#: templates/extras/script_list.html:147 #, python-format msgid "" "Get started by creating a script from " "an uploaded file or data source." msgstr "" -#: netbox/templates/extras/script_result.html:35 -#: netbox/templates/generic/object_list.html:50 netbox/templates/search.html:13 +#: templates/extras/script_result.html:35 templates/generic/object_list.html:50 +#: templates/search.html:13 msgid "Results" msgstr "" -#: netbox/templates/extras/script_result.html:46 +#: templates/extras/script_result.html:46 msgid "Log threshold" msgstr "" -#: netbox/templates/extras/script_result.html:56 +#: templates/extras/script_result.html:56 msgid "All" msgstr "" -#: netbox/templates/extras/tag.html:32 +#: templates/extras/tableconfig.html:10 +msgid "Table Config" +msgstr "" + +#: templates/extras/tableconfig.html:50 +msgid "Columns Displayed" +msgstr "" + +#: templates/extras/tableconfig_edit.html:8 +#: utilities/templates/helpers/table_config_form.html:8 +msgid "Table Configuration" +msgstr "" + +#: templates/extras/tableconfig_edit.html:40 +#: utilities/templates/helpers/table_config_form.html:31 +msgid "Move Up" +msgstr "" + +#: templates/extras/tableconfig_edit.html:43 +#: utilities/templates/helpers/table_config_form.html:34 +msgid "Move Down" +msgstr "" + +#: templates/extras/tag.html:36 msgid "Tagged Items" msgstr "" -#: netbox/templates/extras/tag.html:43 +#: templates/extras/tag.html:47 msgid "Allowed Object Types" msgstr "" -#: netbox/templates/extras/tag.html:51 +#: templates/extras/tag.html:55 msgid "Any" msgstr "" -#: netbox/templates/extras/tag.html:57 +#: templates/extras/tag.html:61 msgid "Tagged Item Types" msgstr "" -#: netbox/templates/extras/tag.html:82 +#: templates/extras/tag.html:86 msgid "Tagged Objects" msgstr "" -#: netbox/templates/extras/webhook.html:26 +#: templates/extras/webhook.html:26 msgid "HTTP Method" msgstr "" -#: netbox/templates/extras/webhook.html:34 +#: templates/extras/webhook.html:34 msgid "HTTP Content Type" msgstr "" -#: netbox/templates/extras/webhook.html:47 +#: templates/extras/webhook.html:47 msgid "SSL Verification" msgstr "" -#: netbox/templates/extras/webhook.html:60 +#: templates/extras/webhook.html:60 msgid "Additional Headers" msgstr "" -#: netbox/templates/extras/webhook.html:70 +#: templates/extras/webhook.html:70 msgid "Body Template" msgstr "" -#: netbox/templates/generic/bulk_add_component.html:29 +#: templates/generic/bulk_add_component.html:29 msgid "Bulk Creation" msgstr "" -#: netbox/templates/generic/bulk_add_component.html:34 -#: netbox/templates/generic/bulk_delete.html:32 -#: netbox/templates/generic/bulk_edit.html:33 +#: templates/generic/bulk_add_component.html:34 +#: templates/generic/bulk_delete.html:32 templates/generic/bulk_edit.html:33 msgid "Selected Objects" msgstr "" -#: netbox/templates/generic/bulk_add_component.html:58 +#: templates/generic/bulk_add_component.html:58 msgid "to Add" msgstr "" -#: netbox/templates/generic/bulk_delete.html:27 +#: templates/generic/bulk_delete.html:27 msgid "Bulk Delete" msgstr "" -#: netbox/templates/generic/bulk_delete.html:49 +#: templates/generic/bulk_delete.html:49 msgid "Confirm Bulk Deletion" msgstr "" -#: netbox/templates/generic/bulk_delete.html:50 +#: templates/generic/bulk_delete.html:50 #, python-format msgid "" "The following operation will delete %(count)s " @@ -13936,82 +13470,79 @@ msgid "" "this action." msgstr "" -#: netbox/templates/generic/bulk_edit.html:21 -#: netbox/templates/generic/object_edit.html:22 +#: templates/generic/bulk_edit.html:21 templates/generic/object_edit.html:22 msgid "Editing" msgstr "" -#: netbox/templates/generic/bulk_edit.html:28 +#: templates/generic/bulk_edit.html:28 msgid "Bulk Edit" msgstr "" -#: netbox/templates/generic/bulk_edit.html:107 -#: netbox/templates/generic/bulk_rename.html:66 +#: templates/generic/bulk_edit.html:107 templates/generic/bulk_rename.html:66 msgid "Apply" msgstr "" -#: netbox/templates/generic/bulk_import.html:19 +#: templates/generic/bulk_import.html:19 msgid "Bulk Import" msgstr "" -#: netbox/templates/generic/bulk_import.html:25 +#: templates/generic/bulk_import.html:25 msgid "Direct Import" msgstr "" -#: netbox/templates/generic/bulk_import.html:30 +#: templates/generic/bulk_import.html:30 msgid "Upload File" msgstr "" -#: netbox/templates/generic/bulk_import.html:58 -#: netbox/templates/generic/bulk_import.html:80 -#: netbox/templates/generic/bulk_import.html:102 +#: templates/generic/bulk_import.html:58 templates/generic/bulk_import.html:80 +#: templates/generic/bulk_import.html:102 msgid "Submit" msgstr "" -#: netbox/templates/generic/bulk_import.html:113 +#: templates/generic/bulk_import.html:113 msgid "Field Options" msgstr "" -#: netbox/templates/generic/bulk_import.html:119 +#: templates/generic/bulk_import.html:119 msgid "Accessor" msgstr "" -#: netbox/templates/generic/bulk_import.html:148 +#: templates/generic/bulk_import.html:148 msgid "choices" msgstr "" -#: netbox/templates/generic/bulk_import.html:161 +#: templates/generic/bulk_import.html:161 msgid "Import Value" msgstr "" -#: netbox/templates/generic/bulk_import.html:181 +#: templates/generic/bulk_import.html:181 msgid "Format: YYYY-MM-DD" msgstr "" -#: netbox/templates/generic/bulk_import.html:183 +#: templates/generic/bulk_import.html:183 msgid "Specify true or false" msgstr "" -#: netbox/templates/generic/bulk_import.html:195 +#: templates/generic/bulk_import.html:195 msgid "Required fields must be specified for all objects." msgstr "" -#: netbox/templates/generic/bulk_import.html:201 +#: templates/generic/bulk_import.html:201 #, python-format msgid "" "Related objects may be referenced by any unique attribute. For example, " "%(example)s would identify a VRF by its route distinguisher." msgstr "" -#: netbox/templates/generic/bulk_remove.html:28 +#: templates/generic/bulk_remove.html:28 msgid "Bulk Remove" msgstr "" -#: netbox/templates/generic/bulk_remove.html:42 +#: templates/generic/bulk_remove.html:42 msgid "Confirm Bulk Removal" msgstr "" -#: netbox/templates/generic/bulk_remove.html:43 +#: templates/generic/bulk_remove.html:43 #, python-format msgid "" "The following operation will remove %(count)s %(obj_type_plural)s from " @@ -14019,143 +13550,143 @@ msgid "" "removed and confirm below." msgstr "" -#: netbox/templates/generic/bulk_remove.html:64 +#: templates/generic/bulk_remove.html:64 #, python-format msgid "Remove these %(count)s %(obj_type_plural)s" msgstr "" -#: netbox/templates/generic/bulk_rename.html:20 +#: templates/generic/bulk_rename.html:20 msgid "Renaming" msgstr "" -#: netbox/templates/generic/bulk_rename.html:27 +#: templates/generic/bulk_rename.html:27 msgid "Bulk Rename" msgstr "" -#: netbox/templates/generic/bulk_rename.html:39 +#: templates/generic/bulk_rename.html:39 msgid "Current Name" msgstr "" -#: netbox/templates/generic/bulk_rename.html:40 +#: templates/generic/bulk_rename.html:40 msgid "New Name" msgstr "" -#: netbox/templates/generic/bulk_rename.html:64 -#: netbox/utilities/templates/widgets/markdown_input.html:11 +#: templates/generic/bulk_rename.html:64 +#: utilities/templates/widgets/markdown_input.html:11 msgid "Preview" msgstr "" -#: netbox/templates/generic/confirmation_form.html:16 +#: templates/generic/confirmation_form.html:16 msgid "Are you sure" msgstr "" -#: netbox/templates/generic/confirmation_form.html:20 +#: templates/generic/confirmation_form.html:20 msgid "Confirm" msgstr "" -#: netbox/templates/generic/object_children.html:47 -#: netbox/utilities/templates/buttons/bulk_edit.html:4 +#: templates/generic/object_children.html:47 +#: utilities/templates/buttons/bulk_edit.html:4 msgid "Edit Selected" msgstr "" -#: netbox/templates/generic/object_children.html:61 -#: netbox/utilities/templates/buttons/bulk_delete.html:4 +#: templates/generic/object_children.html:61 +#: utilities/templates/buttons/bulk_delete.html:4 msgid "Delete Selected" msgstr "" -#: netbox/templates/generic/object_edit.html:24 +#: templates/generic/object_edit.html:24 #, python-format msgid "Add a new %(object_type)s" msgstr "" -#: netbox/templates/generic/object_edit.html:35 +#: templates/generic/object_edit.html:35 msgid "View model documentation" msgstr "" -#: netbox/templates/generic/object_edit.html:36 +#: templates/generic/object_edit.html:36 msgid "Help" msgstr "" -#: netbox/templates/generic/object_edit.html:83 +#: templates/generic/object_edit.html:83 msgid "Create & Add Another" msgstr "" -#: netbox/templates/generic/object_list.html:57 +#: templates/generic/object_list.html:57 msgid "Filters" msgstr "" -#: netbox/templates/generic/object_list.html:88 +#: templates/generic/object_list.html:88 #, python-format msgid "" "Select all %(count)s " "%(object_type_plural)s matching query" msgstr "" -#: netbox/templates/home.html:15 +#: templates/home.html:15 msgid "New Release Available" msgstr "" -#: netbox/templates/home.html:16 +#: templates/home.html:16 msgid "is available" msgstr "" -#: netbox/templates/home.html:18 +#: templates/home.html:18 msgctxt "Document title" msgid "Upgrade Instructions" msgstr "" -#: netbox/templates/home.html:40 +#: templates/home.html:40 msgid "Unlock Dashboard" msgstr "" -#: netbox/templates/home.html:49 +#: templates/home.html:49 msgid "Lock Dashboard" msgstr "" -#: netbox/templates/home.html:60 +#: templates/home.html:60 msgid "Add Widget" msgstr "" -#: netbox/templates/home.html:63 +#: templates/home.html:63 msgid "Save Layout" msgstr "" -#: netbox/templates/htmx/delete_form.html:7 +#: templates/htmx/delete_form.html:7 msgid "Confirm Deletion" msgstr "" -#: netbox/templates/htmx/delete_form.html:11 +#: templates/htmx/delete_form.html:11 #, python-format msgid "" "Are you sure you want to delete " "%(object_type)s %(object)s?" msgstr "" -#: netbox/templates/htmx/delete_form.html:17 +#: templates/htmx/delete_form.html:17 msgid "The following objects will be deleted as a result of this action." msgstr "" -#: netbox/templates/htmx/notifications.html:15 +#: templates/htmx/notifications.html:15 msgid "ago" msgstr "" -#: netbox/templates/htmx/notifications.html:26 +#: templates/htmx/notifications.html:26 msgid "No unread notifications" msgstr "" -#: netbox/templates/htmx/notifications.html:31 +#: templates/htmx/notifications.html:31 msgid "All notifications" msgstr "" -#: netbox/templates/htmx/object_selector.html:5 +#: templates/htmx/object_selector.html:5 msgid "Select" msgstr "" -#: netbox/templates/htmx/quick_add.html:7 +#: templates/htmx/quick_add.html:7 msgid "Quick Add" msgstr "" -#: netbox/templates/htmx/quick_add_created.html:18 +#: templates/htmx/quick_add_created.html:18 #, python-format msgid "" "\n" @@ -14163,322 +13694,321 @@ msgid "" " " msgstr "" -#: netbox/templates/inc/filter_list.html:43 -#: netbox/utilities/templates/helpers/table_config_form.html:39 +#: templates/inc/filter_list.html:43 +#: utilities/templates/helpers/table_config_form.html:39 msgid "Reset" msgstr "" -#: netbox/templates/inc/light_toggle.html:4 +#: templates/inc/light_toggle.html:4 msgid "Enable dark mode" msgstr "" -#: netbox/templates/inc/light_toggle.html:7 +#: templates/inc/light_toggle.html:7 msgid "Enable light mode" msgstr "" -#: netbox/templates/inc/missing_prerequisites.html:8 +#: templates/inc/missing_prerequisites.html:8 #, python-format msgid "" "Before you can add a %(model)s you must first create a " "%(prerequisite_model)s." msgstr "" -#: netbox/templates/inc/paginator.html:15 +#: templates/inc/paginator.html:15 msgid "Page selection" msgstr "" -#: netbox/templates/inc/paginator.html:75 +#: templates/inc/paginator.html:75 #, python-format msgid "Showing %(start)s-%(end)s of %(total)s" msgstr "" -#: netbox/templates/inc/paginator.html:82 +#: templates/inc/paginator.html:82 msgid "Pagination options" msgstr "" -#: netbox/templates/inc/paginator.html:86 +#: templates/inc/paginator.html:86 msgid "Per Page" msgstr "" -#: netbox/templates/inc/panels/image_attachments.html:10 +#: templates/inc/panels/image_attachments.html:10 msgid "Attach an image" msgstr "" -#: netbox/templates/inc/panels/related_objects.html:5 +#: templates/inc/panels/related_objects.html:5 msgid "Related Objects" msgstr "" -#: netbox/templates/inc/panels/tags.html:11 +#: templates/inc/panels/tags.html:11 msgid "No tags assigned" msgstr "" -#: netbox/templates/inc/sync_warning.html:10 +#: templates/inc/sync_warning.html:10 msgid "Data is out of sync with upstream file" msgstr "" -#: netbox/templates/inc/table_controls_htmx.html:7 +#: templates/inc/table_controls_htmx.html:7 msgid "Quick search" msgstr "" -#: netbox/templates/inc/table_controls_htmx.html:20 +#: templates/inc/table_controls_htmx.html:20 msgid "Saved filter" msgstr "" -#: netbox/templates/inc/table_htmx.html:18 +#: templates/inc/table_htmx.html:18 msgid "Clear ordering" msgstr "" -#: netbox/templates/inc/user_menu.html:6 +#: templates/inc/user_menu.html:6 msgid "Help center" msgstr "" -#: netbox/templates/inc/user_menu.html:56 +#: templates/inc/user_menu.html:56 msgid "Log Out" msgstr "" -#: netbox/templates/inc/user_menu.html:63 netbox/templates/login.html:38 +#: templates/inc/user_menu.html:63 templates/login.html:39 +#: templates/login.html:84 msgid "Log In" msgstr "" -#: netbox/templates/ipam/aggregate.html:14 -#: netbox/templates/ipam/ipaddress.html:14 -#: netbox/templates/ipam/iprange.html:13 netbox/templates/ipam/prefix.html:15 +#: templates/ipam/aggregate.html:14 templates/ipam/ipaddress.html:14 +#: templates/ipam/iprange.html:13 templates/ipam/prefix.html:15 msgid "Family" msgstr "" -#: netbox/templates/ipam/aggregate.html:39 +#: templates/ipam/aggregate.html:39 msgid "Date Added" msgstr "" -#: netbox/templates/ipam/aggregate/prefixes.html:8 -#: netbox/templates/ipam/prefix/prefixes.html:8 -#: netbox/templates/ipam/role.html:10 +#: templates/ipam/aggregate/prefixes.html:8 +#: templates/ipam/prefix/prefixes.html:8 templates/ipam/role.html:10 msgid "Add Prefix" msgstr "" -#: netbox/templates/ipam/asn.html:23 +#: templates/ipam/asn.html:23 msgid "AS Number" msgstr "" -#: netbox/templates/ipam/fhrpgroup.html:52 +#: templates/ipam/fhrpgroup.html:52 msgid "Authentication Type" msgstr "" -#: netbox/templates/ipam/fhrpgroup.html:56 +#: templates/ipam/fhrpgroup.html:56 msgid "Authentication Key" msgstr "" -#: netbox/templates/ipam/fhrpgroup.html:69 +#: templates/ipam/fhrpgroup.html:70 msgid "Virtual IP Addresses" msgstr "" -#: netbox/templates/ipam/inc/ipaddress_edit_header.html:13 +#: templates/ipam/inc/ipaddress_edit_header.html:13 msgid "Assign IP" msgstr "" -#: netbox/templates/ipam/inc/ipaddress_edit_header.html:19 +#: templates/ipam/inc/ipaddress_edit_header.html:19 msgid "Bulk Create" msgstr "" -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:10 +#: templates/ipam/inc/panels/fhrp_groups.html:10 msgid "Create Group" msgstr "" -#: netbox/templates/ipam/inc/panels/fhrp_groups.html:25 +#: templates/ipam/inc/panels/fhrp_groups.html:25 msgid "Virtual IPs" msgstr "" -#: netbox/templates/ipam/inc/toggle_available.html:7 +#: templates/ipam/inc/toggle_available.html:7 msgid "Show Assigned" msgstr "" -#: netbox/templates/ipam/inc/toggle_available.html:10 +#: templates/ipam/inc/toggle_available.html:10 msgid "Show Available" msgstr "" -#: netbox/templates/ipam/inc/toggle_available.html:13 +#: templates/ipam/inc/toggle_available.html:13 msgid "Show All" msgstr "" -#: netbox/templates/ipam/ipaddress.html:23 -#: netbox/templates/ipam/iprange.html:45 netbox/templates/ipam/prefix.html:24 +#: templates/ipam/ipaddress.html:23 templates/ipam/iprange.html:52 +#: templates/ipam/prefix.html:24 msgid "Global" msgstr "" -#: netbox/templates/ipam/ipaddress.html:85 +#: templates/ipam/ipaddress.html:85 msgid "NAT (outside)" msgstr "" -#: netbox/templates/ipam/ipaddress_assign.html:8 +#: templates/ipam/ipaddress_assign.html:8 msgid "Assign an IP Address" msgstr "" -#: netbox/templates/ipam/ipaddress_assign.html:22 +#: templates/ipam/ipaddress_assign.html:22 msgid "Select IP Address" msgstr "" -#: netbox/templates/ipam/ipaddress_assign.html:35 +#: templates/ipam/ipaddress_assign.html:35 msgid "Search Results" msgstr "" -#: netbox/templates/ipam/ipaddress_bulk_add.html:6 +#: templates/ipam/ipaddress_bulk_add.html:6 msgid "Bulk Add IP Addresses" msgstr "" -#: netbox/templates/ipam/iprange.html:17 +#: templates/ipam/iprange.html:17 msgid "Starting Address" msgstr "" -#: netbox/templates/ipam/iprange.html:21 +#: templates/ipam/iprange.html:21 msgid "Ending Address" msgstr "" -#: netbox/templates/ipam/iprange.html:33 netbox/templates/ipam/prefix.html:106 -msgid "Marked fully utilized" -msgstr "" - -#: netbox/templates/ipam/prefix.html:95 +#: templates/ipam/prefix.html:95 msgid "Addressing Details" msgstr "" -#: netbox/templates/ipam/prefix.html:114 +#: templates/ipam/prefix.html:106 +msgid "Marked fully utilized" +msgstr "" + +#: templates/ipam/prefix.html:114 msgid "Child IPs" msgstr "" -#: netbox/templates/ipam/prefix.html:122 +#: templates/ipam/prefix.html:122 msgid "Available IPs" msgstr "" -#: netbox/templates/ipam/prefix.html:134 +#: templates/ipam/prefix.html:134 msgid "First available IP" msgstr "" -#: netbox/templates/ipam/prefix.html:175 +#: templates/ipam/prefix.html:175 msgid "Prefix Details" msgstr "" -#: netbox/templates/ipam/prefix.html:181 +#: templates/ipam/prefix.html:181 msgid "Network Address" msgstr "" -#: netbox/templates/ipam/prefix.html:185 +#: templates/ipam/prefix.html:185 msgid "Network Mask" msgstr "" -#: netbox/templates/ipam/prefix.html:189 +#: templates/ipam/prefix.html:189 msgid "Wildcard Mask" msgstr "" -#: netbox/templates/ipam/prefix.html:193 +#: templates/ipam/prefix.html:193 msgid "Broadcast Address" msgstr "" -#: netbox/templates/ipam/prefix/ip_ranges.html:7 +#: templates/ipam/prefix/ip_ranges.html:7 msgid "Add IP Range" msgstr "" -#: netbox/templates/ipam/prefix_list.html:7 +#: templates/ipam/prefix_list.html:7 msgid "Hide Depth Indicators" msgstr "" -#: netbox/templates/ipam/prefix_list.html:11 +#: templates/ipam/prefix_list.html:11 msgid "Max Depth" msgstr "" -#: netbox/templates/ipam/prefix_list.html:28 +#: templates/ipam/prefix_list.html:28 msgid "Max Length" msgstr "" -#: netbox/templates/ipam/rir.html:10 +#: templates/ipam/rir.html:10 msgid "Add Aggregate" msgstr "" -#: netbox/templates/ipam/routetarget.html:38 +#: templates/ipam/routetarget.html:38 msgid "Importing VRFs" msgstr "" -#: netbox/templates/ipam/routetarget.html:44 +#: templates/ipam/routetarget.html:44 msgid "Exporting VRFs" msgstr "" -#: netbox/templates/ipam/routetarget.html:52 +#: templates/ipam/routetarget.html:52 msgid "Importing L2VPNs" msgstr "" -#: netbox/templates/ipam/routetarget.html:58 +#: templates/ipam/routetarget.html:58 msgid "Exporting L2VPNs" msgstr "" -#: netbox/templates/ipam/vlan.html:66 +#: templates/ipam/vlan.html:66 msgid "Q-in-Q Role" msgstr "" -#: netbox/templates/ipam/vlan.html:104 +#: templates/ipam/vlan.html:104 msgid "Add a Prefix" msgstr "" -#: netbox/templates/ipam/vlan.html:114 +#: templates/ipam/vlan.html:114 msgid "Customer VLANs" msgstr "" -#: netbox/templates/ipam/vlan.html:118 +#: templates/ipam/vlan.html:118 msgid "Add a VLAN" msgstr "" -#: netbox/templates/ipam/vlangroup.html:18 +#: templates/ipam/vlangroup.html:18 msgid "Add VLAN" msgstr "" -#: netbox/templates/ipam/vlantranslationpolicy.html:51 +#: templates/ipam/vlantranslationpolicy.html:51 msgid "Add Rule" msgstr "" -#: netbox/templates/ipam/vrf.html:16 +#: templates/ipam/vrf.html:16 msgid "Route Distinguisher" msgstr "" -#: netbox/templates/ipam/vrf.html:29 +#: templates/ipam/vrf.html:29 msgid "Unique IP Space" msgstr "" -#: netbox/templates/login.html:29 -#: netbox/utilities/templates/form_helpers/render_errors.html:7 +#: templates/login.html:29 +#: utilities/templates/form_helpers/render_errors.html:7 msgid "Errors" msgstr "" -#: netbox/templates/login.html:69 +#: templates/login.html:70 msgid "Sign In" msgstr "" -#: netbox/templates/login.html:77 +#: templates/login.html:80 msgctxt "Denotes an alternative option" msgid "Or" msgstr "" -#: netbox/templates/media_failure.html:7 +#: templates/media_failure.html:7 msgid "Static Media Failure - NetBox" msgstr "" -#: netbox/templates/media_failure.html:21 +#: templates/media_failure.html:21 msgid "Static Media Failure" msgstr "" -#: netbox/templates/media_failure.html:23 +#: templates/media_failure.html:23 msgid "The following static media file failed to load" msgstr "" -#: netbox/templates/media_failure.html:26 +#: templates/media_failure.html:26 msgid "Check the following" msgstr "" -#: netbox/templates/media_failure.html:29 +#: templates/media_failure.html:29 msgid "" "manage.py collectstatic was run during the most recent upgrade. " "This installs the most recent iteration of each static file into the static " "root path." msgstr "" -#: netbox/templates/media_failure.html:35 +#: templates/media_failure.html:35 #, python-format msgid "" "The HTTP service (e.g. nginx or Apache) is configured to serve files from " @@ -14486,1899 +14016,1892 @@ msgid "" "installation documentation for further guidance." msgstr "" -#: netbox/templates/media_failure.html:47 +#: templates/media_failure.html:47 #, python-format msgid "" "The file %(filename)s exists in the static root directory and " "is readable by the HTTP server." msgstr "" -#: netbox/templates/media_failure.html:55 +#: templates/media_failure.html:55 #, python-format msgid "" "Click here to attempt loading NetBox again." msgstr "" -#: netbox/templates/tenancy/contact.html:18 netbox/tenancy/filtersets.py:147 -#: netbox/tenancy/forms/bulk_edit.py:138 netbox/tenancy/forms/filtersets.py:102 -#: netbox/tenancy/forms/forms.py:57 netbox/tenancy/forms/model_forms.py:106 -#: netbox/tenancy/forms/model_forms.py:130 netbox/tenancy/tables/contacts.py:98 +#: templates/tenancy/contact.html:18 tenancy/filtersets.py:152 +#: tenancy/forms/bulk_edit.py:149 tenancy/forms/filtersets.py:102 +#: tenancy/forms/forms.py:57 tenancy/forms/model_forms.py:108 +#: tenancy/forms/model_forms.py:132 tenancy/tables/contacts.py:102 msgid "Contact" msgstr "" -#: netbox/templates/tenancy/contact.html:29 -#: netbox/tenancy/forms/bulk_edit.py:99 +#: templates/tenancy/contact.html:39 tenancy/forms/bulk_edit.py:106 msgid "Title" msgstr "" -#: netbox/templates/tenancy/contact.html:33 -#: netbox/tenancy/forms/bulk_edit.py:104 netbox/tenancy/tables/contacts.py:64 +#: templates/tenancy/contact.html:43 tenancy/forms/bulk_edit.py:111 +#: tenancy/tables/contacts.py:68 msgid "Phone" msgstr "" -#: netbox/templates/tenancy/contactgroup.html:18 -#: netbox/tenancy/forms/forms.py:67 netbox/tenancy/forms/model_forms.py:75 +#: templates/tenancy/contactgroup.html:18 tenancy/forms/forms.py:67 +#: tenancy/forms/model_forms.py:77 msgid "Contact Group" msgstr "" -#: netbox/templates/tenancy/contactgroup.html:50 +#: templates/tenancy/contactgroup.html:51 msgid "Add Contact Group" msgstr "" -#: netbox/templates/tenancy/contactrole.html:15 -#: netbox/tenancy/filtersets.py:152 netbox/tenancy/forms/forms.py:62 -#: netbox/tenancy/forms/model_forms.py:87 +#: templates/tenancy/contactrole.html:15 tenancy/filtersets.py:157 +#: tenancy/forms/forms.py:62 tenancy/forms/model_forms.py:89 msgid "Contact Role" msgstr "" -#: netbox/templates/tenancy/object_contacts.html:9 +#: templates/tenancy/object_contacts.html:9 msgid "Add a contact" msgstr "" -#: netbox/templates/tenancy/tenantgroup.html:17 +#: templates/tenancy/tenantgroup.html:17 msgid "Add Tenant" msgstr "" -#: netbox/templates/tenancy/tenantgroup.html:26 -#: netbox/tenancy/forms/model_forms.py:32 netbox/tenancy/tables/columns.py:36 -#: netbox/tenancy/tables/columns.py:46 +#: templates/tenancy/tenantgroup.html:26 tenancy/forms/model_forms.py:33 +#: tenancy/tables/columns.py:36 tenancy/tables/columns.py:46 msgid "Tenant Group" msgstr "" -#: netbox/templates/tenancy/tenantgroup.html:59 +#: templates/tenancy/tenantgroup.html:60 msgid "Add Tenant Group" msgstr "" -#: netbox/templates/users/group.html:39 netbox/templates/users/user.html:63 +#: templates/users/group.html:39 templates/users/user.html:63 msgid "Assigned Permissions" msgstr "" -#: netbox/templates/users/objectpermission.html:6 -#: netbox/templates/users/objectpermission.html:14 -#: netbox/users/forms/filtersets.py:66 +#: templates/users/objectpermission.html:6 +#: templates/users/objectpermission.html:14 users/forms/filtersets.py:66 msgid "Permission" msgstr "" -#: netbox/templates/users/objectpermission.html:34 +#: templates/users/objectpermission.html:34 msgid "View" msgstr "" -#: netbox/templates/users/objectpermission.html:52 -#: netbox/users/forms/model_forms.py:315 +#: templates/users/objectpermission.html:52 users/forms/model_forms.py:315 msgid "Constraints" msgstr "" -#: netbox/templates/users/objectpermission.html:72 +#: templates/users/objectpermission.html:72 msgid "Assigned Users" msgstr "" -#: netbox/templates/virtualization/cluster.html:56 +#: templates/virtualization/cluster.html:56 msgid "Allocated Resources" msgstr "" -#: netbox/templates/virtualization/cluster.html:59 -#: netbox/templates/virtualization/virtualmachine.html:125 +#: templates/virtualization/cluster.html:59 +#: templates/virtualization/virtualmachine.html:125 msgid "Virtual CPUs" msgstr "" -#: netbox/templates/virtualization/cluster.html:63 -#: netbox/templates/virtualization/virtualmachine.html:129 +#: templates/virtualization/cluster.html:63 +#: templates/virtualization/virtualmachine.html:129 msgid "Memory" msgstr "" -#: netbox/templates/virtualization/cluster.html:73 -#: netbox/templates/virtualization/virtualmachine.html:140 +#: templates/virtualization/cluster.html:73 +#: templates/virtualization/virtualmachine.html:140 msgid "Disk Space" msgstr "" -#: netbox/templates/virtualization/cluster/base.html:18 +#: templates/virtualization/cluster/base.html:18 msgid "Add Virtual Machine" msgstr "" -#: netbox/templates/virtualization/cluster/base.html:24 +#: templates/virtualization/cluster/base.html:24 msgid "Assign Device" msgstr "" -#: netbox/templates/virtualization/cluster/devices.html:10 +#: templates/virtualization/cluster/devices.html:10 msgid "Remove Selected" msgstr "" -#: netbox/templates/virtualization/cluster_add_devices.html:9 +#: templates/virtualization/cluster_add_devices.html:9 #, python-format msgid "Add Device to Cluster %(cluster)s" msgstr "" -#: netbox/templates/virtualization/cluster_add_devices.html:23 +#: templates/virtualization/cluster_add_devices.html:23 msgid "Device Selection" msgstr "" -#: netbox/templates/virtualization/cluster_add_devices.html:31 +#: templates/virtualization/cluster_add_devices.html:31 msgid "Add Devices" msgstr "" -#: netbox/templates/virtualization/clustergroup.html:10 -#: netbox/templates/virtualization/clustertype.html:10 +#: templates/virtualization/clustergroup.html:10 +#: templates/virtualization/clustertype.html:10 msgid "Add Cluster" msgstr "" -#: netbox/templates/virtualization/clustergroup.html:19 -#: netbox/virtualization/forms/model_forms.py:53 +#: templates/virtualization/clustergroup.html:19 +#: virtualization/forms/model_forms.py:53 msgid "Cluster Group" msgstr "" -#: netbox/templates/virtualization/clustertype.html:19 -#: netbox/templates/virtualization/virtualmachine.html:110 -#: netbox/virtualization/forms/model_forms.py:39 +#: templates/virtualization/clustertype.html:19 +#: templates/virtualization/virtualmachine.html:110 +#: virtualization/forms/model_forms.py:39 msgid "Cluster Type" msgstr "" -#: netbox/templates/virtualization/virtualdisk.html:18 +#: templates/virtualization/virtualdisk.html:18 msgid "Virtual Disk" msgstr "" -#: netbox/templates/virtualization/virtualmachine.html:122 -#: netbox/virtualization/forms/bulk_edit.py:172 -#: netbox/virtualization/forms/model_forms.py:231 +#: templates/virtualization/virtualmachine.html:122 +#: virtualization/forms/bulk_edit.py:172 +#: virtualization/forms/model_forms.py:231 msgid "Resources" msgstr "" -#: netbox/templates/virtualization/virtualmachine.html:178 +#: templates/virtualization/virtualmachine.html:178 msgid "Add Virtual Disk" msgstr "" -#: netbox/templates/vpn/ikepolicy.html:10 -#: netbox/templates/vpn/ipsecprofile.html:33 netbox/vpn/tables/crypto.py:166 +#: templates/vpn/ikepolicy.html:10 templates/vpn/ipsecprofile.html:33 +#: vpn/tables/crypto.py:166 msgid "IKE Policy" msgstr "" -#: netbox/templates/vpn/ikepolicy.html:21 +#: templates/vpn/ikepolicy.html:21 msgid "IKE Version" msgstr "" -#: netbox/templates/vpn/ikepolicy.html:29 +#: templates/vpn/ikepolicy.html:29 msgid "Pre-Shared Key" msgstr "" -#: netbox/templates/vpn/ikepolicy.html:33 -#: netbox/templates/wireless/inc/authentication_attrs.html:20 +#: templates/vpn/ikepolicy.html:33 +#: templates/wireless/inc/authentication_attrs.html:20 msgid "Show Secret" msgstr "" -#: netbox/templates/vpn/ikepolicy.html:57 -#: netbox/templates/vpn/ipsecpolicy.html:45 -#: netbox/templates/vpn/ipsecprofile.html:52 -#: netbox/templates/vpn/ipsecprofile.html:77 -#: netbox/vpn/forms/model_forms.py:317 netbox/vpn/forms/model_forms.py:354 -#: netbox/vpn/tables/crypto.py:68 netbox/vpn/tables/crypto.py:134 +#: templates/vpn/ikepolicy.html:57 templates/vpn/ipsecpolicy.html:45 +#: templates/vpn/ipsecprofile.html:52 templates/vpn/ipsecprofile.html:77 +#: vpn/forms/model_forms.py:317 vpn/forms/model_forms.py:354 +#: vpn/tables/crypto.py:68 vpn/tables/crypto.py:134 msgid "Proposals" msgstr "" -#: netbox/templates/vpn/ikeproposal.html:10 +#: templates/vpn/ikeproposal.html:10 msgid "IKE Proposal" msgstr "" -#: netbox/templates/vpn/ikeproposal.html:21 netbox/vpn/forms/bulk_edit.py:97 -#: netbox/vpn/forms/bulk_import.py:145 netbox/vpn/forms/filtersets.py:106 +#: templates/vpn/ikeproposal.html:21 vpn/forms/bulk_edit.py:97 +#: vpn/forms/bulk_import.py:145 vpn/forms/filtersets.py:106 msgid "Authentication method" msgstr "" -#: netbox/templates/vpn/ikeproposal.html:25 -#: netbox/templates/vpn/ipsecproposal.html:21 netbox/vpn/forms/bulk_edit.py:102 -#: netbox/vpn/forms/bulk_edit.py:172 netbox/vpn/forms/bulk_import.py:149 -#: netbox/vpn/forms/bulk_import.py:195 netbox/vpn/forms/filtersets.py:111 -#: netbox/vpn/forms/filtersets.py:159 +#: templates/vpn/ikeproposal.html:25 templates/vpn/ipsecproposal.html:21 +#: vpn/forms/bulk_edit.py:102 vpn/forms/bulk_edit.py:172 +#: vpn/forms/bulk_import.py:149 vpn/forms/bulk_import.py:195 +#: vpn/forms/filtersets.py:111 vpn/forms/filtersets.py:159 msgid "Encryption algorithm" msgstr "" -#: netbox/templates/vpn/ikeproposal.html:29 -#: netbox/templates/vpn/ipsecproposal.html:25 netbox/vpn/forms/bulk_edit.py:107 -#: netbox/vpn/forms/bulk_edit.py:177 netbox/vpn/forms/bulk_import.py:153 -#: netbox/vpn/forms/bulk_import.py:200 netbox/vpn/forms/filtersets.py:116 -#: netbox/vpn/forms/filtersets.py:164 +#: templates/vpn/ikeproposal.html:29 templates/vpn/ipsecproposal.html:25 +#: vpn/forms/bulk_edit.py:107 vpn/forms/bulk_edit.py:177 +#: vpn/forms/bulk_import.py:153 vpn/forms/bulk_import.py:200 +#: vpn/forms/filtersets.py:116 vpn/forms/filtersets.py:164 msgid "Authentication algorithm" msgstr "" -#: netbox/templates/vpn/ikeproposal.html:33 +#: templates/vpn/ikeproposal.html:33 msgid "DH group" msgstr "" -#: netbox/templates/vpn/ikeproposal.html:37 -#: netbox/templates/vpn/ipsecproposal.html:29 netbox/vpn/forms/bulk_edit.py:182 -#: netbox/vpn/models/crypto.py:146 +#: templates/vpn/ikeproposal.html:37 templates/vpn/ipsecproposal.html:29 +#: vpn/forms/bulk_edit.py:182 vpn/models/crypto.py:146 msgid "SA lifetime (seconds)" msgstr "" -#: netbox/templates/vpn/ipsecpolicy.html:10 -#: netbox/templates/vpn/ipsecprofile.html:66 netbox/vpn/tables/crypto.py:170 +#: templates/vpn/ipsecpolicy.html:10 templates/vpn/ipsecprofile.html:66 +#: vpn/tables/crypto.py:170 msgid "IPSec Policy" msgstr "" -#: netbox/templates/vpn/ipsecpolicy.html:21 netbox/vpn/forms/bulk_edit.py:210 -#: netbox/vpn/models/crypto.py:191 +#: templates/vpn/ipsecpolicy.html:21 vpn/forms/bulk_edit.py:210 +#: vpn/models/crypto.py:191 msgid "PFS group" msgstr "" -#: netbox/templates/vpn/ipsecprofile.html:10 netbox/vpn/forms/model_forms.py:55 +#: templates/vpn/ipsecprofile.html:10 vpn/forms/model_forms.py:55 msgid "IPSec Profile" msgstr "" -#: netbox/templates/vpn/ipsecprofile.html:89 netbox/vpn/tables/crypto.py:137 +#: templates/vpn/ipsecprofile.html:89 vpn/tables/crypto.py:137 msgid "PFS Group" msgstr "" -#: netbox/templates/vpn/ipsecproposal.html:10 +#: templates/vpn/ipsecproposal.html:10 msgid "IPSec Proposal" msgstr "" -#: netbox/templates/vpn/ipsecproposal.html:33 netbox/vpn/forms/bulk_edit.py:186 -#: netbox/vpn/models/crypto.py:152 +#: templates/vpn/ipsecproposal.html:33 vpn/forms/bulk_edit.py:186 +#: vpn/models/crypto.py:152 msgid "SA lifetime (KB)" msgstr "" -#: netbox/templates/vpn/l2vpn.html:11 -#: netbox/templates/vpn/l2vpntermination.html:9 +#: templates/vpn/l2vpn.html:11 templates/vpn/l2vpntermination.html:9 msgid "L2VPN Attributes" msgstr "" -#: netbox/templates/vpn/l2vpn.html:60 netbox/templates/vpn/tunnel.html:76 +#: templates/vpn/l2vpn.html:64 templates/vpn/tunnel.html:76 msgid "Add a Termination" msgstr "" -#: netbox/templates/vpn/tunnel.html:37 netbox/vpn/forms/bulk_edit.py:49 -#: netbox/vpn/forms/bulk_import.py:48 netbox/vpn/forms/filtersets.py:62 +#: templates/vpn/tunnel.html:37 vpn/forms/bulk_edit.py:49 +#: vpn/forms/bulk_import.py:48 vpn/forms/filtersets.py:62 msgid "Encapsulation" msgstr "" -#: netbox/templates/vpn/tunnel.html:41 netbox/vpn/forms/bulk_edit.py:55 -#: netbox/vpn/forms/bulk_import.py:53 netbox/vpn/forms/filtersets.py:69 -#: netbox/vpn/models/crypto.py:246 netbox/vpn/tables/tunnels.py:51 +#: templates/vpn/tunnel.html:41 vpn/forms/bulk_edit.py:55 +#: vpn/forms/bulk_import.py:53 vpn/forms/filtersets.py:69 +#: vpn/models/crypto.py:246 vpn/tables/tunnels.py:51 msgid "IPSec profile" msgstr "" -#: netbox/templates/vpn/tunnel.html:45 netbox/vpn/forms/bulk_edit.py:69 -#: netbox/vpn/forms/filtersets.py:73 +#: templates/vpn/tunnel.html:45 vpn/forms/bulk_edit.py:69 +#: vpn/forms/filtersets.py:73 msgid "Tunnel ID" msgstr "" -#: netbox/templates/vpn/tunnelgroup.html:14 +#: templates/vpn/tunnelgroup.html:14 msgid "Add Tunnel" msgstr "" -#: netbox/templates/vpn/tunnelgroup.html:23 netbox/vpn/forms/model_forms.py:36 -#: netbox/vpn/forms/model_forms.py:49 +#: templates/vpn/tunnelgroup.html:23 vpn/forms/model_forms.py:36 +#: vpn/forms/model_forms.py:49 msgid "Tunnel Group" msgstr "" -#: netbox/templates/vpn/tunneltermination.html:10 +#: templates/vpn/tunneltermination.html:10 msgid "Tunnel Termination" msgstr "" -#: netbox/templates/vpn/tunneltermination.html:35 -#: netbox/vpn/forms/bulk_import.py:107 netbox/vpn/forms/model_forms.py:103 -#: netbox/vpn/forms/model_forms.py:139 netbox/vpn/forms/model_forms.py:248 -#: netbox/vpn/tables/tunnels.py:101 +#: templates/vpn/tunneltermination.html:35 vpn/forms/bulk_import.py:107 +#: vpn/forms/model_forms.py:103 vpn/forms/model_forms.py:139 +#: vpn/forms/model_forms.py:248 vpn/tables/tunnels.py:101 msgid "Outside IP" msgstr "" -#: netbox/templates/vpn/tunneltermination.html:51 +#: templates/vpn/tunneltermination.html:51 msgid "Peer Terminations" msgstr "" -#: netbox/templates/wireless/inc/authentication_attrs.html:12 +#: templates/wireless/inc/authentication_attrs.html:12 msgid "Cipher" msgstr "" -#: netbox/templates/wireless/inc/authentication_attrs.html:16 +#: templates/wireless/inc/authentication_attrs.html:16 msgid "PSK" msgstr "" -#: netbox/templates/wireless/inc/wirelesslink_interface.html:35 -#: netbox/templates/wireless/inc/wirelesslink_interface.html:45 +#: templates/wireless/inc/wirelesslink_interface.html:35 +#: templates/wireless/inc/wirelesslink_interface.html:45 msgctxt "Abbreviation for megahertz" msgid "MHz" msgstr "" -#: netbox/templates/wireless/wirelesslan.html:65 +#: templates/wireless/wirelesslan.html:65 msgid "Attached Interfaces" msgstr "" -#: netbox/templates/wireless/wirelesslangroup.html:17 +#: templates/wireless/wirelesslangroup.html:17 msgid "Add Wireless LAN" msgstr "" -#: netbox/templates/wireless/wirelesslangroup.html:26 -#: netbox/wireless/forms/model_forms.py:29 +#: templates/wireless/wirelesslangroup.html:26 wireless/forms/model_forms.py:30 msgid "Wireless LAN Group" msgstr "" -#: netbox/templates/wireless/wirelesslangroup.html:59 +#: templates/wireless/wirelesslangroup.html:60 msgid "Add Wireless LAN Group" msgstr "" -#: netbox/templates/wireless/wirelesslink.html:14 +#: templates/wireless/wirelesslink.html:14 msgid "Link Properties" msgstr "" -#: netbox/tenancy/filtersets.py:28 +#: tenancy/filtersets.py:28 msgid "Parent contact group (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:34 +#: tenancy/filtersets.py:34 msgid "Parent contact group (slug)" msgstr "" -#: netbox/tenancy/filtersets.py:40 netbox/tenancy/filtersets.py:67 -#: netbox/tenancy/filtersets.py:110 +#: tenancy/filtersets.py:40 tenancy/filtersets.py:72 tenancy/filtersets.py:115 msgid "Contact group (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:47 netbox/tenancy/filtersets.py:74 -#: netbox/tenancy/filtersets.py:117 +#: tenancy/filtersets.py:47 tenancy/filtersets.py:79 tenancy/filtersets.py:122 msgid "Contact group (slug)" msgstr "" -#: netbox/tenancy/filtersets.py:104 +#: tenancy/filtersets.py:52 tenancy/filtersets.py:109 msgid "Contact (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:121 +#: tenancy/filtersets.py:126 msgid "Contact role (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:127 +#: tenancy/filtersets.py:132 msgid "Contact role (slug)" msgstr "" -#: netbox/tenancy/filtersets.py:158 +#: tenancy/filtersets.py:163 msgid "Contact group" msgstr "" -#: netbox/tenancy/filtersets.py:169 +#: tenancy/filtersets.py:174 msgid "Parent tenant group (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:175 +#: tenancy/filtersets.py:180 msgid "Parent tenant group (slug)" msgstr "" -#: netbox/tenancy/filtersets.py:181 netbox/tenancy/filtersets.py:201 +#: tenancy/filtersets.py:186 tenancy/filtersets.py:206 msgid "Tenant group (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:234 +#: tenancy/filtersets.py:239 msgid "Tenant Group (ID)" msgstr "" -#: netbox/tenancy/filtersets.py:241 +#: tenancy/filtersets.py:246 msgid "Tenant Group (slug)" msgstr "" -#: netbox/tenancy/forms/bulk_edit.py:66 +#: tenancy/forms/bulk_edit.py:67 msgid "Desciption" msgstr "" -#: netbox/tenancy/forms/bulk_import.py:101 +#: tenancy/forms/bulk_edit.py:96 +msgid "Add groups" +msgstr "" + +#: tenancy/forms/bulk_edit.py:101 +msgid "Remove groups" +msgstr "" + +#: tenancy/forms/bulk_import.py:84 +msgid "" +"Group names separated by commas, encased with double quotes (e.g. \"Group 1," +"Group 2\")" +msgstr "" + +#: tenancy/forms/bulk_import.py:100 msgid "Assigned contact" msgstr "" -#: netbox/tenancy/models/contacts.py:32 +#: tenancy/models/contacts.py:33 msgid "contact group" msgstr "" -#: netbox/tenancy/models/contacts.py:33 +#: tenancy/models/contacts.py:34 msgid "contact groups" msgstr "" -#: netbox/tenancy/models/contacts.py:42 +#: tenancy/models/contacts.py:43 msgid "contact role" msgstr "" -#: netbox/tenancy/models/contacts.py:43 +#: tenancy/models/contacts.py:44 msgid "contact roles" msgstr "" -#: netbox/tenancy/models/contacts.py:63 +#: tenancy/models/contacts.py:64 msgid "title" msgstr "" -#: netbox/tenancy/models/contacts.py:68 +#: tenancy/models/contacts.py:69 msgid "phone" msgstr "" -#: netbox/tenancy/models/contacts.py:73 +#: tenancy/models/contacts.py:74 msgid "email" msgstr "" -#: netbox/tenancy/models/contacts.py:82 +#: tenancy/models/contacts.py:83 msgid "link" msgstr "" -#: netbox/tenancy/models/contacts.py:98 +#: tenancy/models/contacts.py:93 msgid "contact" msgstr "" -#: netbox/tenancy/models/contacts.py:99 +#: tenancy/models/contacts.py:94 msgid "contacts" msgstr "" -#: netbox/tenancy/models/contacts.py:146 +#: tenancy/models/contacts.py:108 +msgid "contact group membership" +msgstr "" + +#: tenancy/models/contacts.py:109 +msgid "contact group memberships" +msgstr "" + +#: tenancy/models/contacts.py:153 msgid "contact assignment" msgstr "" -#: netbox/tenancy/models/contacts.py:147 +#: tenancy/models/contacts.py:154 msgid "contact assignments" msgstr "" -#: netbox/tenancy/models/contacts.py:163 +#: tenancy/models/contacts.py:170 #, python-brace-format msgid "Contacts cannot be assigned to this object type ({type})." msgstr "" -#: netbox/tenancy/models/tenants.py:32 +#: tenancy/models/tenants.py:32 msgid "tenant group" msgstr "" -#: netbox/tenancy/models/tenants.py:33 +#: tenancy/models/tenants.py:33 msgid "tenant groups" msgstr "" -#: netbox/tenancy/models/tenants.py:68 +#: tenancy/models/tenants.py:68 msgid "Tenant name must be unique per group." msgstr "" -#: netbox/tenancy/models/tenants.py:78 +#: tenancy/models/tenants.py:78 msgid "Tenant slug must be unique per group." msgstr "" -#: netbox/tenancy/models/tenants.py:86 +#: tenancy/models/tenants.py:86 msgid "tenant" msgstr "" -#: netbox/tenancy/models/tenants.py:87 +#: tenancy/models/tenants.py:87 msgid "tenants" msgstr "" -#: netbox/tenancy/tables/contacts.py:112 +#: tenancy/tables/contacts.py:116 msgid "Contact Title" msgstr "" -#: netbox/tenancy/tables/contacts.py:116 +#: tenancy/tables/contacts.py:120 msgid "Contact Phone" msgstr "" -#: netbox/tenancy/tables/contacts.py:121 +#: tenancy/tables/contacts.py:125 msgid "Contact Email" msgstr "" -#: netbox/tenancy/tables/contacts.py:125 +#: tenancy/tables/contacts.py:129 msgid "Contact Address" msgstr "" -#: netbox/tenancy/tables/contacts.py:129 +#: tenancy/tables/contacts.py:133 msgid "Contact Link" msgstr "" -#: netbox/tenancy/tables/contacts.py:134 +#: tenancy/tables/contacts.py:138 msgid "Contact Description" msgstr "" -#: netbox/users/filtersets.py:33 netbox/users/filtersets.py:73 +#: users/filtersets.py:33 users/filtersets.py:73 msgid "Permission (ID)" msgstr "" -#: netbox/users/filtersets.py:38 netbox/users/filtersets.py:78 +#: users/filtersets.py:38 users/filtersets.py:78 msgid "Notification group (ID)" msgstr "" -#: netbox/users/forms/bulk_edit.py:26 +#: users/forms/bulk_edit.py:26 msgid "First name" msgstr "" -#: netbox/users/forms/bulk_edit.py:31 +#: users/forms/bulk_edit.py:31 msgid "Last name" msgstr "" -#: netbox/users/forms/bulk_edit.py:43 +#: users/forms/bulk_edit.py:43 msgid "Staff status" msgstr "" -#: netbox/users/forms/bulk_edit.py:48 +#: users/forms/bulk_edit.py:48 msgid "Superuser status" msgstr "" -#: netbox/users/forms/bulk_import.py:41 +#: users/forms/bulk_import.py:41 msgid "If no key is provided, one will be generated automatically." msgstr "" -#: netbox/users/forms/filtersets.py:51 netbox/users/tables.py:42 +#: users/forms/filtersets.py:51 users/tables.py:42 msgid "Is Staff" msgstr "" -#: netbox/users/forms/filtersets.py:58 netbox/users/tables.py:45 +#: users/forms/filtersets.py:58 users/tables.py:45 msgid "Is Superuser" msgstr "" -#: netbox/users/forms/filtersets.py:91 netbox/users/tables.py:86 +#: users/forms/filtersets.py:91 users/tables.py:86 msgid "Can View" msgstr "" -#: netbox/users/forms/filtersets.py:98 netbox/users/tables.py:89 +#: users/forms/filtersets.py:98 users/tables.py:89 msgid "Can Add" msgstr "" -#: netbox/users/forms/filtersets.py:105 netbox/users/tables.py:92 +#: users/forms/filtersets.py:105 users/tables.py:92 msgid "Can Change" msgstr "" -#: netbox/users/forms/filtersets.py:112 netbox/users/tables.py:95 +#: users/forms/filtersets.py:112 users/tables.py:95 msgid "Can Delete" msgstr "" -#: netbox/users/forms/model_forms.py:62 +#: users/forms/model_forms.py:62 msgid "User Interface" msgstr "" -#: netbox/users/forms/model_forms.py:114 +#: users/forms/model_forms.py:114 msgid "" "Keys must be at least 40 characters in length. Be sure to record " "your key prior to submitting this form, as it may no longer be " "accessible once the token has been created." msgstr "" -#: netbox/users/forms/model_forms.py:126 +#: users/forms/model_forms.py:126 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for " "no restrictions. Example: 10.1.1.0/24,192.168.10.16/32,2001:" "db8:1::/64" msgstr "" -#: netbox/users/forms/model_forms.py:175 +#: users/forms/model_forms.py:175 msgid "Confirm password" msgstr "" -#: netbox/users/forms/model_forms.py:178 +#: users/forms/model_forms.py:178 msgid "Enter the same password as before, for verification." msgstr "" -#: netbox/users/forms/model_forms.py:227 +#: users/forms/model_forms.py:227 msgid "Passwords do not match! Please check your input and try again." msgstr "" -#: netbox/users/forms/model_forms.py:294 +#: users/forms/model_forms.py:294 msgid "Additional actions" msgstr "" -#: netbox/users/forms/model_forms.py:297 +#: users/forms/model_forms.py:297 msgid "Actions granted in addition to those listed above" msgstr "" -#: netbox/users/forms/model_forms.py:313 +#: users/forms/model_forms.py:313 msgid "Objects" msgstr "" -#: netbox/users/forms/model_forms.py:325 +#: users/forms/model_forms.py:325 msgid "" "JSON expression of a queryset filter that will return only permitted " "objects. Leave null to match all objects of this type. A list of multiple " "objects will result in a logical OR operation." msgstr "" -#: netbox/users/forms/model_forms.py:364 +#: users/forms/model_forms.py:364 msgid "At least one action must be selected." msgstr "" -#: netbox/users/forms/model_forms.py:382 +#: users/forms/model_forms.py:382 #, python-brace-format msgid "Invalid filter for {model}: {error}" msgstr "" -#: netbox/users/models/permissions.py:39 +#: users/models/permissions.py:37 msgid "The list of actions granted by this permission" msgstr "" -#: netbox/users/models/permissions.py:44 +#: users/models/permissions.py:42 msgid "constraints" msgstr "" -#: netbox/users/models/permissions.py:45 +#: users/models/permissions.py:43 msgid "Queryset filter matching the applicable objects of the selected type(s)" msgstr "" -#: netbox/users/models/permissions.py:52 +#: users/models/permissions.py:50 msgid "permission" msgstr "" -#: netbox/users/models/permissions.py:53 netbox/users/models/users.py:47 +#: users/models/permissions.py:51 users/models/users.py:47 msgid "permissions" msgstr "" -#: netbox/users/models/preferences.py:29 netbox/users/models/preferences.py:30 +#: users/models/preferences.py:29 users/models/preferences.py:30 msgid "user preferences" msgstr "" -#: netbox/users/models/preferences.py:97 +#: users/models/preferences.py:97 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "" -#: netbox/users/models/preferences.py:109 +#: users/models/preferences.py:109 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "" -#: netbox/users/models/tokens.py:36 +#: users/models/tokens.py:36 msgid "expires" msgstr "" -#: netbox/users/models/tokens.py:41 +#: users/models/tokens.py:41 msgid "last used" msgstr "" -#: netbox/users/models/tokens.py:46 +#: users/models/tokens.py:46 msgid "key" msgstr "" -#: netbox/users/models/tokens.py:52 +#: users/models/tokens.py:52 msgid "write enabled" msgstr "" -#: netbox/users/models/tokens.py:54 +#: users/models/tokens.py:54 msgid "Permit create/update/delete operations using this key" msgstr "" -#: netbox/users/models/tokens.py:65 +#: users/models/tokens.py:65 msgid "allowed IPs" msgstr "" -#: netbox/users/models/tokens.py:67 +#: users/models/tokens.py:67 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for " "no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" msgstr "" -#: netbox/users/models/tokens.py:75 +#: users/models/tokens.py:75 msgid "token" msgstr "" -#: netbox/users/models/tokens.py:76 +#: users/models/tokens.py:76 msgid "tokens" msgstr "" -#: netbox/users/models/users.py:57 netbox/vpn/models/crypto.py:43 +#: users/models/users.py:57 vpn/models/crypto.py:43 msgid "group" msgstr "" -#: netbox/users/models/users.py:92 +#: users/models/users.py:92 msgid "user" msgstr "" -#: netbox/users/models/users.py:104 +#: users/models/users.py:104 msgid "A user with this username already exists." msgstr "" -#: netbox/users/tables.py:98 +#: users/tables.py:98 msgid "Custom Actions" msgstr "" -#: netbox/utilities/api.py:153 +#: utilities/api.py:153 #, python-brace-format msgid "Related object not found using the provided attributes: {params}" msgstr "" -#: netbox/utilities/api.py:156 +#: utilities/api.py:156 #, python-brace-format msgid "Multiple objects match the provided attributes: {params}" msgstr "" -#: netbox/utilities/api.py:168 +#: utilities/api.py:168 #, python-brace-format msgid "" "Related objects must be referenced by numeric ID or by dictionary of " "attributes. Received an unrecognized value: {value}" msgstr "" -#: netbox/utilities/api.py:177 +#: utilities/api.py:177 #, python-brace-format msgid "Related object not found using the provided numeric ID: {id}" msgstr "" -#: netbox/utilities/choices.py:19 +#: utilities/choices.py:23 #, python-brace-format msgid "{name} has a key defined but CHOICES is not a list" msgstr "" -#: netbox/utilities/conversion.py:20 +#: utilities/conversion.py:20 msgid "Weight must be a positive number" msgstr "" -#: netbox/utilities/conversion.py:22 +#: utilities/conversion.py:22 #, python-brace-format msgid "Invalid value '{weight}' for weight (must be a number)" msgstr "" -#: netbox/utilities/conversion.py:33 netbox/utilities/conversion.py:64 +#: utilities/conversion.py:33 utilities/conversion.py:64 #, python-brace-format msgid "Unknown unit {unit}. Must be one of the following: {valid_units}" msgstr "" -#: netbox/utilities/conversion.py:47 +#: utilities/conversion.py:47 #, python-brace-format msgid "Invalid value '{length}' for length (must be a number)" msgstr "" -#: netbox/utilities/conversion.py:49 +#: utilities/conversion.py:49 msgid "Length must be a positive number" msgstr "" -#: netbox/utilities/error_handlers.py:31 +#: utilities/error_handlers.py:31 #, python-brace-format msgid "" "Unable to delete {objects}. {count} dependent objects were " "found: " msgstr "" -#: netbox/utilities/error_handlers.py:33 +#: utilities/error_handlers.py:33 msgid "More than 50" msgstr "" -#: netbox/utilities/fields.py:34 +#: utilities/fields.py:34 msgid "RGB color in hexadecimal. Example: " msgstr "" -#: netbox/utilities/fields.py:163 +#: utilities/fields.py:163 #, python-format msgid "" "%s(%r) is invalid. to_model parameter to CounterCacheField must be a string " "in the format 'app.model'" msgstr "" -#: netbox/utilities/fields.py:173 +#: utilities/fields.py:173 #, python-format msgid "" "%s(%r) is invalid. to_field parameter to CounterCacheField must be a string " "in the format 'field'" msgstr "" -#: netbox/utilities/forms/bulk_import.py:23 +#: utilities/forms/bulk_import.py:23 msgid "Enter object data in CSV, JSON or YAML format." msgstr "" -#: netbox/utilities/forms/bulk_import.py:36 +#: utilities/forms/bulk_import.py:36 msgid "CSV delimiter" msgstr "" -#: netbox/utilities/forms/bulk_import.py:37 +#: utilities/forms/bulk_import.py:37 msgid "The character which delimits CSV fields. Applies only to CSV format." msgstr "" -#: netbox/utilities/forms/bulk_import.py:51 +#: utilities/forms/bulk_import.py:51 msgid "Form data must be empty when uploading/selecting a file." msgstr "" -#: netbox/utilities/forms/bulk_import.py:80 +#: utilities/forms/bulk_import.py:80 #, python-brace-format msgid "Unknown data format: {format}" msgstr "" -#: netbox/utilities/forms/bulk_import.py:100 +#: utilities/forms/bulk_import.py:100 msgid "Unable to detect data format. Please specify." msgstr "" -#: netbox/utilities/forms/bulk_import.py:123 +#: utilities/forms/bulk_import.py:123 msgid "Invalid CSV delimiter" msgstr "" -#: netbox/utilities/forms/bulk_import.py:167 +#: utilities/forms/bulk_import.py:167 msgid "" "Invalid YAML data. Data must be in the form of multiple documents, or a " "single document comprising a list of dictionaries." msgstr "" -#: netbox/utilities/forms/fields/array.py:20 +#: utilities/forms/fields/array.py:20 #, python-brace-format msgid "" "Invalid list ({value}). Must be numeric and ranges must be in ascending " "order." msgstr "" -#: netbox/utilities/forms/fields/array.py:40 +#: utilities/forms/fields/array.py:40 msgid "" "Specify one or more numeric ranges separated by commas. Example: " "1-5,20-30" msgstr "" -#: netbox/utilities/forms/fields/array.py:47 +#: utilities/forms/fields/array.py:47 #, python-brace-format msgid "" "Invalid ranges ({value}). Must be a range of integers in ascending order." msgstr "" -#: netbox/utilities/forms/fields/csv.py:44 +#: utilities/forms/fields/csv.py:44 #, python-brace-format msgid "Invalid value for a multiple choice field: {value}" msgstr "" -#: netbox/utilities/forms/fields/csv.py:57 -#: netbox/utilities/forms/fields/csv.py:78 +#: utilities/forms/fields/csv.py:57 utilities/forms/fields/csv.py:78 #, python-format msgid "Object not found: %(value)s" msgstr "" -#: netbox/utilities/forms/fields/csv.py:65 +#: utilities/forms/fields/csv.py:65 #, python-brace-format msgid "" "\"{value}\" is not a unique value for this field; multiple objects were found" msgstr "" -#: netbox/utilities/forms/fields/csv.py:69 +#: utilities/forms/fields/csv.py:69 #, python-brace-format msgid "\"{field_name}\" is an invalid accessor field name." msgstr "" -#: netbox/utilities/forms/fields/csv.py:102 +#: utilities/forms/fields/csv.py:102 msgid "Object type must be specified as \".\"" msgstr "" -#: netbox/utilities/forms/fields/csv.py:106 +#: utilities/forms/fields/csv.py:106 msgid "Invalid object type" msgstr "" -#: netbox/utilities/forms/fields/expandable.py:25 +#: utilities/forms/fields/expandable.py:25 msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " "within a single range are not supported (example: [ge,xe]-0/0/[0-9])." msgstr "" -#: netbox/utilities/forms/fields/expandable.py:46 +#: utilities/forms/fields/expandable.py:46 msgid "" "Specify a numeric range to create multiple IPs.
Example: 192.0.2." "[1,5,100-254]/24" msgstr "" -#: netbox/utilities/forms/fields/fields.py:31 +#: utilities/forms/fields/fields.py:31 #, python-brace-format msgid "" " Markdown syntax is supported" msgstr "" -#: netbox/utilities/forms/fields/fields.py:48 +#: utilities/forms/fields/fields.py:48 msgid "URL-friendly unique shorthand" msgstr "" -#: netbox/utilities/forms/fields/fields.py:104 +#: utilities/forms/fields/fields.py:104 msgid "Enter context data in JSON format." msgstr "" -#: netbox/utilities/forms/fields/fields.py:125 +#: utilities/forms/fields/fields.py:125 msgid "MAC address must be in EUI-48 format" msgstr "" -#: netbox/utilities/forms/forms.py:52 +#: utilities/forms/forms.py:52 msgid "Use regular expressions" msgstr "" -#: netbox/utilities/forms/forms.py:75 +#: utilities/forms/forms.py:75 msgid "" "Numeric ID of an existing object to update (if not creating a new object)" msgstr "" -#: netbox/utilities/forms/forms.py:92 +#: utilities/forms/forms.py:92 #, python-brace-format msgid "Unrecognized header: {name}" msgstr "" -#: netbox/utilities/forms/forms.py:118 -msgid "Available Columns" -msgstr "" - -#: netbox/utilities/forms/forms.py:126 -msgid "Selected Columns" -msgstr "" - -#: netbox/utilities/forms/mixins.py:44 +#: utilities/forms/mixins.py:44 msgid "" "This object has been modified since the form was rendered. Please consult " "the object's change log for details." msgstr "" -#: netbox/utilities/forms/utils.py:42 netbox/utilities/forms/utils.py:68 -#: netbox/utilities/forms/utils.py:85 netbox/utilities/forms/utils.py:87 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "" -#: netbox/utilities/forms/utils.py:74 +#: utilities/forms/utils.py:74 #, python-brace-format msgid "" "Invalid range: Ending value ({end}) must be greater than beginning value " "({begin})." msgstr "" -#: netbox/utilities/forms/utils.py:235 +#: utilities/forms/utils.py:235 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "" -#: netbox/utilities/forms/utils.py:241 +#: utilities/forms/utils.py:241 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "" -#: netbox/utilities/forms/utils.py:250 +#: utilities/forms/utils.py:250 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" -#: netbox/utilities/forms/utils.py:273 +#: utilities/forms/utils.py:273 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "" -#: netbox/utilities/forms/utils.py:275 +#: utilities/forms/utils.py:275 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "" -#: netbox/utilities/forms/utils.py:279 +#: utilities/forms/utils.py:279 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "" -#: netbox/utilities/forms/utils.py:287 +#: utilities/forms/utils.py:287 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "" -#: netbox/utilities/forms/widgets/apiselect.py:133 +#: utilities/forms/widgets/apiselect.py:133 #, python-brace-format msgid "Missing required value for dynamic query param: '{dynamic_params}'" msgstr "" -#: netbox/utilities/forms/widgets/apiselect.py:150 +#: utilities/forms/widgets/apiselect.py:150 #, python-brace-format msgid "Missing required value for static query param: '{static_params}'" msgstr "" -#: netbox/utilities/password_validation.py:13 +#: utilities/jsonschema.py:159 +msgid "Invalid JSON schema definition" +msgstr "" + +#: utilities/jsonschema.py:161 +msgid "JSON schema must define properties" +msgstr "" + +#: utilities/jsonschema.py:166 +#, python-brace-format +msgid "Invalid JSON schema definition: {error}" +msgstr "" + +#: utilities/password_validation.py:13 msgid "Password must have at least one numeral." msgstr "" -#: netbox/utilities/password_validation.py:18 +#: utilities/password_validation.py:18 msgid "Password must have at least one uppercase letter." msgstr "" -#: netbox/utilities/password_validation.py:23 +#: utilities/password_validation.py:23 msgid "Password must have at least one lowercase letter." msgstr "" -#: netbox/utilities/password_validation.py:27 +#: utilities/password_validation.py:27 msgid "" "Your password must contain at least one numeral, one uppercase letter and " "one lowercase letter." msgstr "" -#: netbox/utilities/permissions.py:42 +#: utilities/permissions.py:42 #, python-brace-format msgid "" "Invalid permission name: {name}. Must be in the format ." "_" msgstr "" -#: netbox/utilities/permissions.py:60 +#: utilities/permissions.py:60 #, python-brace-format msgid "Unknown app_label/model_name for {name}" msgstr "" -#: netbox/utilities/request.py:79 +#: utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "" -#: netbox/utilities/tables.py:47 +#: utilities/tables.py:75 #, python-brace-format msgid "A column named {name} is already defined for table {table_name}" msgstr "" -#: netbox/utilities/templates/builtins/customfield_value.html:30 +#: utilities/templates/builtins/customfield_value.html:30 msgid "Not defined" msgstr "" -#: netbox/utilities/templates/buttons/bookmark.html:9 +#: utilities/templates/buttons/bookmark.html:9 msgid "Unbookmark" msgstr "" -#: netbox/utilities/templates/buttons/bookmark.html:13 +#: utilities/templates/buttons/bookmark.html:13 msgid "Bookmark" msgstr "" -#: netbox/utilities/templates/buttons/clone.html:4 +#: utilities/templates/buttons/clone.html:4 msgid "Clone" msgstr "" -#: netbox/utilities/templates/buttons/export.html:7 +#: utilities/templates/buttons/export.html:7 msgid "Current View" msgstr "" -#: netbox/utilities/templates/buttons/export.html:8 +#: utilities/templates/buttons/export.html:8 msgid "All Data" msgstr "" -#: netbox/utilities/templates/buttons/export.html:28 +#: utilities/templates/buttons/export.html:28 msgid "Add export template" msgstr "" -#: netbox/utilities/templates/buttons/import.html:4 +#: utilities/templates/buttons/import.html:4 msgid "Import" msgstr "" -#: netbox/utilities/templates/buttons/subscribe.html:10 +#: utilities/templates/buttons/subscribe.html:10 msgid "Unsubscribe" msgstr "" -#: netbox/utilities/templates/buttons/subscribe.html:14 +#: utilities/templates/buttons/subscribe.html:14 msgid "Subscribe" msgstr "" -#: netbox/utilities/templates/form_helpers/render_field.html:41 +#: utilities/templates/form_helpers/render_field.html:41 msgid "Copy to clipboard" msgstr "" -#: netbox/utilities/templates/form_helpers/render_field.html:57 +#: utilities/templates/form_helpers/render_field.html:57 msgid "This field is required" msgstr "" -#: netbox/utilities/templates/form_helpers/render_field.html:70 +#: utilities/templates/form_helpers/render_field.html:70 msgid "Set Null" msgstr "" -#: netbox/utilities/templates/helpers/applied_filters.html:11 +#: utilities/templates/helpers/applied_filters.html:11 msgid "Clear all" msgstr "" -#: netbox/utilities/templates/helpers/table_config_form.html:8 -msgid "Table Configuration" -msgstr "" - -#: netbox/utilities/templates/helpers/table_config_form.html:31 -msgid "Move Up" -msgstr "" - -#: netbox/utilities/templates/helpers/table_config_form.html:34 -msgid "Move Down" -msgstr "" - -#: netbox/utilities/templates/navigation/menu.html:14 +#: utilities/templates/navigation/menu.html:14 msgid "Search…" msgstr "" -#: netbox/utilities/templates/navigation/menu.html:14 +#: utilities/templates/navigation/menu.html:14 msgid "Search NetBox" msgstr "" -#: netbox/utilities/templates/widgets/apiselect.html:8 +#: utilities/templates/widgets/apiselect.html:8 msgid "Open selector" msgstr "" -#: netbox/utilities/templates/widgets/apiselect.html:22 +#: utilities/templates/widgets/apiselect.html:22 msgid "Quick add" msgstr "" -#: netbox/utilities/templates/widgets/markdown_input.html:6 +#: utilities/templates/widgets/markdown_input.html:6 msgid "Write" msgstr "" -#: netbox/utilities/testing/views.py:632 +#: utilities/testing/views.py:632 msgid "The test must define csv_update_data." msgstr "" -#: netbox/utilities/validators.py:65 +#: utilities/validators.py:71 +#, python-brace-format +msgid "{value} must be a multiple of {multiple}." +msgstr "" + +#: utilities/validators.py:83 #, python-brace-format msgid "{value} is not a valid regular expression." msgstr "" -#: netbox/utilities/views.py:57 +#: utilities/views.py:57 #, python-brace-format msgid "{self.__class__.__name__} must implement get_required_permission()" msgstr "" -#: netbox/utilities/views.py:93 +#: utilities/views.py:93 #, python-brace-format msgid "{class_name} must implement get_required_permission()" msgstr "" -#: netbox/utilities/views.py:117 +#: utilities/views.py:117 #, python-brace-format msgid "" "{class_name} has no queryset defined. ObjectPermissionRequiredMixin may only " "be used on views which define a base queryset" msgstr "" -#: netbox/virtualization/choices.py:50 +#: virtualization/choices.py:50 msgid "Paused" msgstr "" -#: netbox/virtualization/filtersets.py:45 +#: virtualization/filtersets.py:45 msgid "Parent group (ID)" msgstr "" -#: netbox/virtualization/filtersets.py:51 +#: virtualization/filtersets.py:51 msgid "Parent group (slug)" msgstr "" -#: netbox/virtualization/filtersets.py:55 -#: netbox/virtualization/filtersets.py:107 +#: virtualization/filtersets.py:55 virtualization/filtersets.py:107 msgid "Cluster type (ID)" msgstr "" -#: netbox/virtualization/filtersets.py:117 -#: netbox/virtualization/filtersets.py:237 +#: virtualization/filtersets.py:117 virtualization/filtersets.py:239 msgid "Cluster (ID)" msgstr "" -#: netbox/virtualization/forms/bulk_edit.py:148 -#: netbox/virtualization/models/virtualmachines.py:110 +#: virtualization/forms/bulk_edit.py:148 +#: virtualization/models/virtualmachines.py:109 msgid "vCPUs" msgstr "" -#: netbox/virtualization/forms/bulk_edit.py:152 +#: virtualization/forms/bulk_edit.py:152 msgid "Memory (MB)" msgstr "" -#: netbox/virtualization/forms/bulk_edit.py:156 +#: virtualization/forms/bulk_edit.py:156 msgid "Disk (MB)" msgstr "" -#: netbox/virtualization/forms/bulk_edit.py:324 -#: netbox/virtualization/forms/filtersets.py:269 +#: virtualization/forms/bulk_edit.py:324 virtualization/forms/filtersets.py:269 msgid "Size (MB)" msgstr "" -#: netbox/virtualization/forms/bulk_import.py:45 +#: virtualization/forms/bulk_import.py:45 msgid "Type of cluster" msgstr "" -#: netbox/virtualization/forms/bulk_import.py:52 +#: virtualization/forms/bulk_import.py:52 msgid "Assigned cluster group" msgstr "" -#: netbox/virtualization/forms/bulk_import.py:102 +#: virtualization/forms/bulk_import.py:102 msgid "Assigned cluster" msgstr "" -#: netbox/virtualization/forms/bulk_import.py:109 +#: virtualization/forms/bulk_import.py:109 msgid "Assigned device within cluster" msgstr "" -#: netbox/virtualization/forms/filtersets.py:189 +#: virtualization/forms/filtersets.py:189 msgid "Serial number" msgstr "" -#: netbox/virtualization/forms/model_forms.py:158 +#: virtualization/forms/model_forms.py:158 #, python-brace-format msgid "" "{device} belongs to a different {scope_field} ({device_scope}) than the " "cluster ({cluster_scope})" msgstr "" -#: netbox/virtualization/forms/model_forms.py:199 +#: virtualization/forms/model_forms.py:199 msgid "Optionally pin this VM to a specific host device within the cluster" msgstr "" -#: netbox/virtualization/forms/model_forms.py:228 +#: virtualization/forms/model_forms.py:228 msgid "Site/Cluster" msgstr "" -#: netbox/virtualization/forms/model_forms.py:251 +#: virtualization/forms/model_forms.py:251 msgid "Disk size is managed via the attachment of virtual disks." msgstr "" -#: netbox/virtualization/forms/model_forms.py:405 -#: netbox/virtualization/tables/virtualmachines.py:81 +#: virtualization/forms/model_forms.py:405 +#: virtualization/tables/virtualmachines.py:81 msgid "Disk" msgstr "" -#: netbox/virtualization/models/clusters.py:26 +#: virtualization/models/clusters.py:26 msgid "cluster type" msgstr "" -#: netbox/virtualization/models/clusters.py:27 +#: virtualization/models/clusters.py:27 msgid "cluster types" msgstr "" -#: netbox/virtualization/models/clusters.py:43 +#: virtualization/models/clusters.py:43 msgid "cluster group" msgstr "" -#: netbox/virtualization/models/clusters.py:44 +#: virtualization/models/clusters.py:44 msgid "cluster groups" msgstr "" -#: netbox/virtualization/models/clusters.py:110 +#: virtualization/models/clusters.py:110 msgid "cluster" msgstr "" -#: netbox/virtualization/models/clusters.py:111 +#: virtualization/models/clusters.py:111 msgid "clusters" msgstr "" -#: netbox/virtualization/models/clusters.py:137 +#: virtualization/models/clusters.py:137 #, python-brace-format msgid "" "{count} devices are assigned as hosts for this cluster but are not in site " "{site}" msgstr "" -#: netbox/virtualization/models/clusters.py:144 +#: virtualization/models/clusters.py:144 #, python-brace-format msgid "" "{count} devices are assigned as hosts for this cluster but are not in " "location {location}" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:118 +#: virtualization/models/virtualmachines.py:117 msgid "memory (MB)" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:123 +#: virtualization/models/virtualmachines.py:122 msgid "disk (MB)" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:161 +#: virtualization/models/virtualmachines.py:166 msgid "Virtual machine name must be unique per cluster." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:164 +#: virtualization/models/virtualmachines.py:169 msgid "virtual machine" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:165 +#: virtualization/models/virtualmachines.py:170 msgid "virtual machines" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:176 +#: virtualization/models/virtualmachines.py:181 msgid "A virtual machine must be assigned to a site and/or cluster." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:183 +#: virtualization/models/virtualmachines.py:188 #, python-brace-format msgid "The selected cluster ({cluster}) is not assigned to this site ({site})." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:190 +#: virtualization/models/virtualmachines.py:195 msgid "Must specify a cluster when assigning a host device." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:195 +#: virtualization/models/virtualmachines.py:200 #, python-brace-format msgid "" "The selected device ({device}) is not assigned to this cluster ({cluster})." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:207 +#: virtualization/models/virtualmachines.py:212 #, python-brace-format msgid "" "The specified disk size ({size}) must match the aggregate size of assigned " "virtual disks ({total_size})." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:221 +#: virtualization/models/virtualmachines.py:226 #, python-brace-format msgid "Must be an IPv{family} address. ({ip} is an IPv{version} address.)" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:230 +#: virtualization/models/virtualmachines.py:235 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this VM." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:376 +#: virtualization/models/virtualmachines.py:381 #, python-brace-format msgid "" "The selected parent interface ({parent}) belongs to a different virtual " "machine ({virtual_machine})." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:391 +#: virtualization/models/virtualmachines.py:396 #, python-brace-format msgid "" "The selected bridge interface ({bridge}) belongs to a different virtual " "machine ({virtual_machine})." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:402 +#: virtualization/models/virtualmachines.py:407 #, python-brace-format msgid "" "The untagged VLAN ({untagged_vlan}) must belong to the same site as the " "interface's parent virtual machine, or it must be global." msgstr "" -#: netbox/virtualization/models/virtualmachines.py:414 +#: virtualization/models/virtualmachines.py:419 msgid "size (MB)" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:418 +#: virtualization/models/virtualmachines.py:423 msgid "virtual disk" msgstr "" -#: netbox/virtualization/models/virtualmachines.py:419 +#: virtualization/models/virtualmachines.py:424 msgid "virtual disks" msgstr "" -#: netbox/virtualization/views.py:313 +#: virtualization/views.py:307 #, python-brace-format msgid "Added {count} devices to cluster {cluster}" msgstr "" -#: netbox/virtualization/views.py:348 +#: virtualization/views.py:342 #, python-brace-format msgid "Removed {count} devices from cluster {cluster}" msgstr "" -#: netbox/vpn/choices.py:35 +#: vpn/choices.py:35 msgid "IPsec - Transport" msgstr "" -#: netbox/vpn/choices.py:36 +#: vpn/choices.py:36 msgid "IPsec - Tunnel" msgstr "" -#: netbox/vpn/choices.py:37 +#: vpn/choices.py:37 msgid "IP-in-IP" msgstr "" -#: netbox/vpn/choices.py:38 +#: vpn/choices.py:38 msgid "GRE" msgstr "" -#: netbox/vpn/choices.py:39 +#: vpn/choices.py:39 msgid "WireGuard" msgstr "" -#: netbox/vpn/choices.py:40 +#: vpn/choices.py:40 msgid "OpenVPN" msgstr "" -#: netbox/vpn/choices.py:41 +#: vpn/choices.py:41 msgid "L2TP" msgstr "" -#: netbox/vpn/choices.py:42 +#: vpn/choices.py:42 msgid "PPTP" msgstr "" -#: netbox/vpn/choices.py:88 +#: vpn/choices.py:88 msgid "Aggressive" msgstr "" -#: netbox/vpn/choices.py:89 +#: vpn/choices.py:89 msgid "Main" msgstr "" -#: netbox/vpn/choices.py:100 +#: vpn/choices.py:100 msgid "Pre-shared keys" msgstr "" -#: netbox/vpn/choices.py:101 +#: vpn/choices.py:101 msgid "Certificates" msgstr "" -#: netbox/vpn/choices.py:102 +#: vpn/choices.py:102 msgid "RSA signatures" msgstr "" -#: netbox/vpn/choices.py:103 +#: vpn/choices.py:103 msgid "DSA signatures" msgstr "" -#: netbox/vpn/choices.py:186 netbox/vpn/choices.py:187 -#: netbox/vpn/choices.py:188 netbox/vpn/choices.py:189 -#: netbox/vpn/choices.py:190 netbox/vpn/choices.py:191 -#: netbox/vpn/choices.py:192 netbox/vpn/choices.py:193 -#: netbox/vpn/choices.py:194 netbox/vpn/choices.py:195 -#: netbox/vpn/choices.py:196 netbox/vpn/choices.py:197 -#: netbox/vpn/choices.py:198 netbox/vpn/choices.py:199 -#: netbox/vpn/choices.py:200 netbox/vpn/choices.py:201 -#: netbox/vpn/choices.py:202 netbox/vpn/choices.py:203 -#: netbox/vpn/choices.py:204 netbox/vpn/choices.py:205 -#: netbox/vpn/choices.py:206 netbox/vpn/choices.py:207 -#: netbox/vpn/choices.py:208 netbox/vpn/choices.py:209 +#: vpn/choices.py:186 vpn/choices.py:187 vpn/choices.py:188 vpn/choices.py:189 +#: vpn/choices.py:190 vpn/choices.py:191 vpn/choices.py:192 vpn/choices.py:193 +#: vpn/choices.py:194 vpn/choices.py:195 vpn/choices.py:196 vpn/choices.py:197 +#: vpn/choices.py:198 vpn/choices.py:199 vpn/choices.py:200 vpn/choices.py:201 +#: vpn/choices.py:202 vpn/choices.py:203 vpn/choices.py:204 vpn/choices.py:205 +#: vpn/choices.py:206 vpn/choices.py:207 vpn/choices.py:208 vpn/choices.py:209 #, python-brace-format msgid "Group {n}" msgstr "" -#: netbox/vpn/choices.py:251 +#: vpn/choices.py:252 msgid "Ethernet Private LAN" msgstr "" -#: netbox/vpn/choices.py:252 +#: vpn/choices.py:253 msgid "Ethernet Virtual Private LAN" msgstr "" -#: netbox/vpn/choices.py:255 +#: vpn/choices.py:256 msgid "Ethernet Private Tree" msgstr "" -#: netbox/vpn/choices.py:256 +#: vpn/choices.py:257 msgid "Ethernet Virtual Private Tree" msgstr "" -#: netbox/vpn/filtersets.py:41 +#: vpn/choices.py:260 +msgid "SPB" +msgstr "" + +#: vpn/filtersets.py:41 msgid "Tunnel group (ID)" msgstr "" -#: netbox/vpn/filtersets.py:47 +#: vpn/filtersets.py:47 msgid "Tunnel group (slug)" msgstr "" -#: netbox/vpn/filtersets.py:54 +#: vpn/filtersets.py:54 msgid "IPSec profile (ID)" msgstr "" -#: netbox/vpn/filtersets.py:60 +#: vpn/filtersets.py:60 msgid "IPSec profile (name)" msgstr "" -#: netbox/vpn/filtersets.py:81 +#: vpn/filtersets.py:81 msgid "Tunnel (ID)" msgstr "" -#: netbox/vpn/filtersets.py:87 +#: vpn/filtersets.py:87 msgid "Tunnel (name)" msgstr "" -#: netbox/vpn/filtersets.py:118 +#: vpn/filtersets.py:118 msgid "Outside IP (ID)" msgstr "" -#: netbox/vpn/filtersets.py:130 netbox/vpn/filtersets.py:263 +#: vpn/filtersets.py:130 vpn/filtersets.py:263 msgid "IKE policy (ID)" msgstr "" -#: netbox/vpn/filtersets.py:136 netbox/vpn/filtersets.py:269 +#: vpn/filtersets.py:136 vpn/filtersets.py:269 msgid "IKE policy (name)" msgstr "" -#: netbox/vpn/filtersets.py:200 netbox/vpn/filtersets.py:273 +#: vpn/filtersets.py:200 vpn/filtersets.py:273 msgid "IPSec policy (ID)" msgstr "" -#: netbox/vpn/filtersets.py:206 netbox/vpn/filtersets.py:279 +#: vpn/filtersets.py:206 vpn/filtersets.py:279 msgid "IPSec policy (name)" msgstr "" -#: netbox/vpn/filtersets.py:348 +#: vpn/filtersets.py:351 msgid "L2VPN (slug)" msgstr "" -#: netbox/vpn/filtersets.py:412 +#: vpn/filtersets.py:415 msgid "VM Interface (ID)" msgstr "" -#: netbox/vpn/filtersets.py:418 +#: vpn/filtersets.py:421 msgid "VLAN (name)" msgstr "" -#: netbox/vpn/forms/bulk_edit.py:45 netbox/vpn/forms/bulk_import.py:42 -#: netbox/vpn/forms/filtersets.py:59 +#: vpn/forms/bulk_edit.py:45 vpn/forms/bulk_import.py:42 +#: vpn/forms/filtersets.py:59 msgid "Tunnel group" msgstr "" -#: netbox/vpn/forms/bulk_edit.py:117 netbox/vpn/models/crypto.py:48 +#: vpn/forms/bulk_edit.py:117 vpn/models/crypto.py:48 msgid "SA lifetime" msgstr "" -#: netbox/vpn/forms/bulk_edit.py:151 netbox/wireless/forms/bulk_edit.py:81 -#: netbox/wireless/forms/bulk_edit.py:129 -#: netbox/wireless/forms/filtersets.py:67 -#: netbox/wireless/forms/filtersets.py:126 +#: vpn/forms/bulk_edit.py:151 wireless/forms/bulk_edit.py:82 +#: wireless/forms/bulk_edit.py:130 wireless/forms/filtersets.py:67 +#: wireless/forms/filtersets.py:126 msgid "Pre-shared key" msgstr "" -#: netbox/vpn/forms/bulk_edit.py:237 netbox/vpn/forms/bulk_import.py:239 -#: netbox/vpn/forms/filtersets.py:204 netbox/vpn/forms/model_forms.py:373 -#: netbox/vpn/models/crypto.py:104 +#: vpn/forms/bulk_edit.py:237 vpn/forms/bulk_import.py:239 +#: vpn/forms/filtersets.py:204 vpn/forms/model_forms.py:373 +#: vpn/models/crypto.py:104 msgid "IKE policy" msgstr "" -#: netbox/vpn/forms/bulk_edit.py:242 netbox/vpn/forms/bulk_import.py:244 -#: netbox/vpn/forms/filtersets.py:209 netbox/vpn/forms/model_forms.py:377 -#: netbox/vpn/models/crypto.py:207 +#: vpn/forms/bulk_edit.py:242 vpn/forms/bulk_import.py:244 +#: vpn/forms/filtersets.py:209 vpn/forms/model_forms.py:377 +#: vpn/models/crypto.py:207 msgid "IPSec policy" msgstr "" -#: netbox/vpn/forms/bulk_import.py:50 +#: vpn/forms/bulk_import.py:50 msgid "Tunnel encapsulation" msgstr "" -#: netbox/vpn/forms/bulk_import.py:90 +#: vpn/forms/bulk_import.py:90 msgid "Parent device of assigned interface" msgstr "" -#: netbox/vpn/forms/bulk_import.py:97 +#: vpn/forms/bulk_import.py:97 msgid "Parent VM of assigned interface" msgstr "" -#: netbox/vpn/forms/bulk_import.py:104 +#: vpn/forms/bulk_import.py:104 msgid "Device or virtual machine interface" msgstr "" -#: netbox/vpn/forms/bulk_import.py:183 +#: vpn/forms/bulk_import.py:183 msgid "IKE proposal(s)" msgstr "" -#: netbox/vpn/forms/bulk_import.py:215 netbox/vpn/models/crypto.py:195 +#: vpn/forms/bulk_import.py:215 vpn/models/crypto.py:195 msgid "Diffie-Hellman group for Perfect Forward Secrecy" msgstr "" -#: netbox/vpn/forms/bulk_import.py:222 +#: vpn/forms/bulk_import.py:222 msgid "IPSec proposal(s)" msgstr "" -#: netbox/vpn/forms/bulk_import.py:236 +#: vpn/forms/bulk_import.py:236 msgid "IPSec protocol" msgstr "" -#: netbox/vpn/forms/bulk_import.py:266 +#: vpn/forms/bulk_import.py:271 msgid "L2VPN type" msgstr "" -#: netbox/vpn/forms/bulk_import.py:287 +#: vpn/forms/bulk_import.py:292 msgid "Parent device (for interface)" msgstr "" -#: netbox/vpn/forms/bulk_import.py:294 +#: vpn/forms/bulk_import.py:299 msgid "Parent virtual machine (for interface)" msgstr "" -#: netbox/vpn/forms/bulk_import.py:301 +#: vpn/forms/bulk_import.py:306 msgid "Assigned interface (device or VM)" msgstr "" -#: netbox/vpn/forms/bulk_import.py:334 +#: vpn/forms/bulk_import.py:339 msgid "Cannot import device and VM interface terminations simultaneously." msgstr "" -#: netbox/vpn/forms/bulk_import.py:336 +#: vpn/forms/bulk_import.py:341 msgid "Each termination must specify either an interface or a VLAN." msgstr "" -#: netbox/vpn/forms/bulk_import.py:338 +#: vpn/forms/bulk_import.py:343 msgid "Cannot assign both an interface and a VLAN." msgstr "" -#: netbox/vpn/forms/filtersets.py:135 +#: vpn/forms/filtersets.py:135 msgid "IKE version" msgstr "" -#: netbox/vpn/forms/filtersets.py:147 netbox/vpn/forms/filtersets.py:180 -#: netbox/vpn/forms/model_forms.py:299 netbox/vpn/forms/model_forms.py:336 +#: vpn/forms/filtersets.py:147 vpn/forms/filtersets.py:180 +#: vpn/forms/model_forms.py:299 vpn/forms/model_forms.py:336 msgid "Proposal" msgstr "" -#: netbox/vpn/forms/filtersets.py:257 +#: vpn/forms/filtersets.py:262 msgid "Assigned Object Type" msgstr "" -#: netbox/vpn/forms/model_forms.py:96 netbox/vpn/forms/model_forms.py:131 -#: netbox/vpn/forms/model_forms.py:241 netbox/vpn/tables/tunnels.py:91 +#: vpn/forms/model_forms.py:96 vpn/forms/model_forms.py:131 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 msgid "Tunnel interface" msgstr "" -#: netbox/vpn/forms/model_forms.py:151 +#: vpn/forms/model_forms.py:151 msgid "First Termination" msgstr "" -#: netbox/vpn/forms/model_forms.py:154 +#: vpn/forms/model_forms.py:154 msgid "Second Termination" msgstr "" -#: netbox/vpn/forms/model_forms.py:198 +#: vpn/forms/model_forms.py:198 msgid "This parameter is required when defining a termination." msgstr "" -#: netbox/vpn/forms/model_forms.py:490 +#: vpn/forms/model_forms.py:490 msgid "A termination must specify an interface or VLAN." msgstr "" -#: netbox/vpn/forms/model_forms.py:492 +#: vpn/forms/model_forms.py:492 msgid "" "A termination can only have one terminating object (an interface or VLAN)." msgstr "" -#: netbox/vpn/models/crypto.py:33 +#: vpn/models/crypto.py:33 msgid "encryption algorithm" msgstr "" -#: netbox/vpn/models/crypto.py:37 +#: vpn/models/crypto.py:37 msgid "authentication algorithm" msgstr "" -#: netbox/vpn/models/crypto.py:45 +#: vpn/models/crypto.py:45 msgid "Diffie-Hellman group ID" msgstr "" -#: netbox/vpn/models/crypto.py:51 +#: vpn/models/crypto.py:51 msgid "Security association lifetime (in seconds)" msgstr "" -#: netbox/vpn/models/crypto.py:60 +#: vpn/models/crypto.py:60 msgid "IKE proposal" msgstr "" -#: netbox/vpn/models/crypto.py:61 +#: vpn/models/crypto.py:61 msgid "IKE proposals" msgstr "" -#: netbox/vpn/models/crypto.py:75 +#: vpn/models/crypto.py:75 msgid "version" msgstr "" -#: netbox/vpn/models/crypto.py:88 netbox/vpn/models/crypto.py:188 +#: vpn/models/crypto.py:88 vpn/models/crypto.py:188 msgid "proposals" msgstr "" -#: netbox/vpn/models/crypto.py:91 netbox/wireless/models.py:41 +#: vpn/models/crypto.py:91 wireless/models.py:41 msgid "pre-shared key" msgstr "" -#: netbox/vpn/models/crypto.py:105 +#: vpn/models/crypto.py:105 msgid "IKE policies" msgstr "" -#: netbox/vpn/models/crypto.py:115 +#: vpn/models/crypto.py:115 msgid "Mode is required for selected IKE version" msgstr "" -#: netbox/vpn/models/crypto.py:119 +#: vpn/models/crypto.py:119 msgid "Mode cannot be used for selected IKE version" msgstr "" -#: netbox/vpn/models/crypto.py:134 +#: vpn/models/crypto.py:134 msgid "encryption" msgstr "" -#: netbox/vpn/models/crypto.py:140 +#: vpn/models/crypto.py:140 msgid "authentication" msgstr "" -#: netbox/vpn/models/crypto.py:149 +#: vpn/models/crypto.py:149 msgid "Security association lifetime (seconds)" msgstr "" -#: netbox/vpn/models/crypto.py:155 +#: vpn/models/crypto.py:155 msgid "Security association lifetime (in kilobytes)" msgstr "" -#: netbox/vpn/models/crypto.py:164 +#: vpn/models/crypto.py:164 msgid "IPSec proposal" msgstr "" -#: netbox/vpn/models/crypto.py:165 +#: vpn/models/crypto.py:165 msgid "IPSec proposals" msgstr "" -#: netbox/vpn/models/crypto.py:175 +#: vpn/models/crypto.py:175 msgid "Encryption and/or authentication algorithm must be defined" msgstr "" -#: netbox/vpn/models/crypto.py:208 +#: vpn/models/crypto.py:208 msgid "IPSec policies" msgstr "" -#: netbox/vpn/models/crypto.py:247 +#: vpn/models/crypto.py:247 msgid "IPSec profiles" msgstr "" -#: netbox/vpn/models/l2vpn.py:113 +#: vpn/models/l2vpn.py:117 msgid "L2VPN termination" msgstr "" -#: netbox/vpn/models/l2vpn.py:114 +#: vpn/models/l2vpn.py:118 msgid "L2VPN terminations" msgstr "" -#: netbox/vpn/models/l2vpn.py:129 +#: vpn/models/l2vpn.py:133 #, python-brace-format msgid "L2VPN Termination already assigned ({assigned_object})" msgstr "" -#: netbox/vpn/models/l2vpn.py:141 +#: vpn/models/l2vpn.py:145 #, python-brace-format msgid "" "{l2vpn_type} L2VPNs cannot have more than two terminations; found " "{terminations_count} already defined." msgstr "" -#: netbox/vpn/models/tunnels.py:26 +#: vpn/models/tunnels.py:26 msgid "tunnel group" msgstr "" -#: netbox/vpn/models/tunnels.py:27 +#: vpn/models/tunnels.py:27 msgid "tunnel groups" msgstr "" -#: netbox/vpn/models/tunnels.py:51 +#: vpn/models/tunnels.py:51 msgid "encapsulation" msgstr "" -#: netbox/vpn/models/tunnels.py:70 +#: vpn/models/tunnels.py:70 msgid "tunnel ID" msgstr "" -#: netbox/vpn/models/tunnels.py:92 +#: vpn/models/tunnels.py:92 msgid "tunnel" msgstr "" -#: netbox/vpn/models/tunnels.py:93 +#: vpn/models/tunnels.py:93 msgid "tunnels" msgstr "" -#: netbox/vpn/models/tunnels.py:148 +#: vpn/models/tunnels.py:145 msgid "An object may be terminated to only one tunnel at a time." msgstr "" -#: netbox/vpn/models/tunnels.py:151 +#: vpn/models/tunnels.py:148 msgid "tunnel termination" msgstr "" -#: netbox/vpn/models/tunnels.py:152 +#: vpn/models/tunnels.py:149 msgid "tunnel terminations" msgstr "" -#: netbox/vpn/models/tunnels.py:169 +#: vpn/models/tunnels.py:166 #, python-brace-format msgid "{name} is already attached to a tunnel ({tunnel})." msgstr "" -#: netbox/vpn/tables/crypto.py:22 +#: vpn/tables/crypto.py:22 msgid "Authentication Method" msgstr "" -#: netbox/vpn/tables/crypto.py:25 netbox/vpn/tables/crypto.py:97 +#: vpn/tables/crypto.py:25 vpn/tables/crypto.py:97 msgid "Encryption Algorithm" msgstr "" -#: netbox/vpn/tables/crypto.py:28 netbox/vpn/tables/crypto.py:100 +#: vpn/tables/crypto.py:28 vpn/tables/crypto.py:100 msgid "Authentication Algorithm" msgstr "" -#: netbox/vpn/tables/crypto.py:34 +#: vpn/tables/crypto.py:34 msgid "SA Lifetime" msgstr "" -#: netbox/vpn/tables/crypto.py:71 +#: vpn/tables/crypto.py:71 msgid "Pre-shared Key" msgstr "" -#: netbox/vpn/tables/crypto.py:103 +#: vpn/tables/crypto.py:103 msgid "SA Lifetime (Seconds)" msgstr "" -#: netbox/vpn/tables/crypto.py:106 +#: vpn/tables/crypto.py:106 msgid "SA Lifetime (KB)" msgstr "" -#: netbox/vpn/tables/l2vpn.py:69 +#: vpn/tables/l2vpn.py:72 msgid "Object Parent" msgstr "" -#: netbox/vpn/tables/l2vpn.py:74 +#: vpn/tables/l2vpn.py:77 msgid "Object Site" msgstr "" -#: netbox/wireless/choices.py:11 +#: wireless/choices.py:11 msgid "Access point" msgstr "" -#: netbox/wireless/choices.py:12 +#: wireless/choices.py:12 msgid "Station" msgstr "" -#: netbox/wireless/choices.py:467 +#: wireless/choices.py:467 msgid "Open" msgstr "" -#: netbox/wireless/choices.py:469 +#: wireless/choices.py:469 msgid "WPA Personal (PSK)" msgstr "" -#: netbox/wireless/choices.py:470 +#: wireless/choices.py:470 msgid "WPA Enterprise" msgstr "" -#: netbox/wireless/forms/bulk_edit.py:75 netbox/wireless/forms/bulk_edit.py:123 -#: netbox/wireless/forms/bulk_import.py:70 -#: netbox/wireless/forms/bulk_import.py:73 -#: netbox/wireless/forms/bulk_import.py:115 -#: netbox/wireless/forms/bulk_import.py:118 -#: netbox/wireless/forms/filtersets.py:62 -#: netbox/wireless/forms/filtersets.py:121 +#: wireless/forms/bulk_edit.py:76 wireless/forms/bulk_edit.py:124 +#: wireless/forms/bulk_import.py:70 wireless/forms/bulk_import.py:73 +#: wireless/forms/bulk_import.py:115 wireless/forms/bulk_import.py:118 +#: wireless/forms/filtersets.py:62 wireless/forms/filtersets.py:121 msgid "Authentication cipher" msgstr "" -#: netbox/wireless/forms/bulk_import.py:54 +#: wireless/forms/bulk_import.py:54 msgid "Bridged VLAN" msgstr "" -#: netbox/wireless/forms/bulk_import.py:94 -#: netbox/wireless/tables/wirelesslink.py:27 +#: wireless/forms/bulk_import.py:94 wireless/tables/wirelesslink.py:27 msgid "Interface A" msgstr "" -#: netbox/wireless/forms/bulk_import.py:98 -#: netbox/wireless/tables/wirelesslink.py:36 +#: wireless/forms/bulk_import.py:98 wireless/tables/wirelesslink.py:36 msgid "Interface B" msgstr "" -#: netbox/wireless/forms/model_forms.py:164 +#: wireless/forms/model_forms.py:165 msgid "Side B" msgstr "" -#: netbox/wireless/models.py:32 +#: wireless/models.py:32 msgid "authentication cipher" msgstr "" -#: netbox/wireless/models.py:72 +#: wireless/models.py:72 msgid "wireless LAN group" msgstr "" -#: netbox/wireless/models.py:73 +#: wireless/models.py:73 msgid "wireless LAN groups" msgstr "" -#: netbox/wireless/models.py:116 +#: wireless/models.py:116 msgid "wireless LAN" msgstr "" -#: netbox/wireless/models.py:141 +#: wireless/models.py:134 msgid "interface A" msgstr "" -#: netbox/wireless/models.py:148 +#: wireless/models.py:140 msgid "interface B" msgstr "" -#: netbox/wireless/models.py:196 +#: wireless/models.py:188 msgid "wireless link" msgstr "" -#: netbox/wireless/models.py:197 +#: wireless/models.py:189 msgid "wireless links" msgstr "" -#: netbox/wireless/models.py:212 netbox/wireless/models.py:218 +#: wireless/models.py:204 wireless/models.py:210 #, python-brace-format msgid "{type} is not a wireless interface." msgstr "" -#: netbox/wireless/utils.py:16 +#: wireless/utils.py:16 #, python-brace-format msgid "Invalid channel value: {channel}" msgstr "" -#: netbox/wireless/utils.py:26 +#: wireless/utils.py:26 #, python-brace-format msgid "Invalid channel attribute: {name}" msgstr "" diff --git a/netbox/translations/es/LC_MESSAGES/django.po b/netbox/translations/es/LC_MESSAGES/django.po index bc3a209e0..2fbb34697 100644 --- a/netbox/translations/es/LC_MESSAGES/django.po +++ b/netbox/translations/es/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Spanish (https://app.transifex.com/netbox-community/teams/178115/es/)\n" @@ -64,26 +64,26 @@ msgstr "Utilizado por última vez" msgid "Allowed IPs" msgstr "IP permitidas" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Ha iniciado sesión como {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Has cerrado sesión." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Se han actualizado tus preferencias." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Las credenciales de usuario autenticadas por LDAP no se pueden cambiar en " "NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "La contraseña se ha cambiado correctamente." @@ -131,7 +131,7 @@ msgstr "Retirado" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primaria" @@ -228,7 +228,7 @@ msgstr "Grupo de sitios (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -991,7 +991,7 @@ msgstr "Atributos" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1090,7 +1090,7 @@ msgstr "Red de proveedores" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1210,9 +1210,9 @@ msgstr "Función operativa" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1314,7 +1314,7 @@ msgstr "Contactos" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1337,7 +1337,7 @@ msgstr "Región" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1709,7 +1709,7 @@ msgstr "terminaciones de circuitos virtuales" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1965,9 +1965,9 @@ msgstr "Terminaciones" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2141,7 +2141,7 @@ msgstr "Semanal" msgid "30 days" msgstr "30 días" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Actualizado" @@ -2172,7 +2172,7 @@ msgstr "Detenido" msgid "Cancelled" msgstr "Cancelado" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2809,49 +2809,49 @@ msgstr "ID" msgid "Interval" msgstr "Intervalo" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Versión" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Última actualización" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Versión mínima de NetBox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Versión máxima de NetBox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "No se han encontrado datos de complementos" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "autor" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Instalado" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certificado" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Publicado" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Versión instalada" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Versión más reciente" @@ -3088,8 +3088,8 @@ msgstr "De atrás hacia adelante" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3218,7 +3218,7 @@ msgstr "Virtual" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3229,7 +3229,7 @@ msgid "Virtual interfaces" msgstr "Interfaces virtuales" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3616,7 +3616,7 @@ msgstr "Es de profundidad total" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3765,7 +3765,7 @@ msgstr "VID asignado" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3786,7 +3786,7 @@ msgstr "VID asignado" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3824,7 +3824,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Política de traducción de VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3862,7 +3862,7 @@ msgstr "Interfaz LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "Dirección MAC" @@ -3870,14 +3870,14 @@ msgstr "Dirección MAC" msgid "Primary MAC address (ID)" msgstr "Dirección MAC principal (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Dirección MAC principal" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Contexto de dispositivo virtual" @@ -3954,8 +3954,8 @@ msgstr "Etiquetas" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4002,8 +4002,8 @@ msgstr "Zona horaria" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4166,7 +4166,7 @@ msgstr "Flujo de aire" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4182,7 +4182,7 @@ msgstr "Estante" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4206,15 +4206,24 @@ msgid "Exclude from utilization" msgstr "Excluir de la utilización" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Tipo de dispositivo" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4400,8 +4409,8 @@ msgid "Allocated power draw (watts)" msgstr "Consumo de energía asignado (vatios)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Puerto de alimentación" @@ -4435,7 +4444,7 @@ msgid "Wireless role" msgstr "Función inalámbrica" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4454,7 +4463,7 @@ msgstr "Módulo" msgid "LAG" msgstr "DESFASE" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Contextos de dispositivos virtuales" @@ -4482,21 +4491,21 @@ msgstr "Velocidad" msgid "Mode" msgstr "Modo" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Grupo de VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN sin etiquetar" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4511,16 +4520,16 @@ msgstr "Agregar VLAN etiquetadas" msgid "Remove tagged VLANs" msgstr "Eliminar las VLAN etiquetadas" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "VLAN de servicio Q-in-Q" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Grupo LAN inalámbrico" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4528,29 +4537,29 @@ msgid "Wireless LANs" msgstr "LAN inalámbricas" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Dirigiéndose" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operación" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4558,7 +4567,7 @@ msgid "Related Interfaces" msgstr "Interfaces relacionadas" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4822,13 +4831,13 @@ msgstr "Puerto de alimentación local que alimenta esta toma" msgid "Electrical phase (for three-phase circuits)" msgstr "Fase eléctrica (para circuitos trifásicos)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Interfaz principal" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4894,8 +4903,8 @@ msgstr "Función inalámbrica (AP/estación)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} no está asignado al dispositivo {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Puerto trasero" @@ -5078,7 +5087,7 @@ msgstr "Tipo de alimentación (AC/DC)" msgid "Single or three-phase" msgstr "Monofásico o trifásico" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5089,7 +5098,7 @@ msgstr "IPv4 principal" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Dirección IPv4 con máscara, p. ej. 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5257,7 +5266,7 @@ msgstr "Amable" msgid "Mgmt only" msgstr "Solo administración" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5405,7 +5414,7 @@ msgstr "" msgid "Characteristics" msgstr "Características" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5420,35 +5429,35 @@ msgstr "" "{module}, si está presente, se reemplazará automáticamente por " "el valor de posición al crear un nuevo módulo." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Plantilla de puerto de consola" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Plantilla de puerto de servidor de consola" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Plantilla de puerto frontal" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Plantilla de interfaz" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Plantilla de toma de corriente" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Plantilla de puerto de alimentación" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Plantilla de puerto trasero" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5456,14 +5465,14 @@ msgstr "Plantilla de puerto trasero" msgid "Console Port" msgstr "Puerto de consola" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Puerto de servidor de consola" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5474,7 +5483,7 @@ msgstr "Puerto de servidor de consola" msgid "Front Port" msgstr "Puerto frontal" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5487,40 +5496,40 @@ msgstr "Puerto frontal" msgid "Rear Port" msgstr "Puerto trasero" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Puerto de alimentación" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Toma de corriente" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Asignación de componentes" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Un InventoryItem solo se puede asignar a un único componente." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Interfaz LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtre las VLAN disponibles para la asignación por grupo." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Dispositivo infantil" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5528,37 +5537,37 @@ msgstr "" "Los dispositivos secundarios primero deben crearse y asignarse al sitio y al" " rack del dispositivo principal." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Puerto de consola" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Puerto de servidor de consola" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Puerto frontal" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "toma de corriente" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Artículo de inventario" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Función del artículo de inventario" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Interfaz VM" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5576,13 +5585,13 @@ msgstr "Interfaz VM" msgid "Virtual Machine" msgstr "Máquina virtual" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Una dirección MAC solo se puede asignar a un único objeto." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5600,18 +5609,18 @@ msgstr "" "{pattern_count} se esperan." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Puertos traseros" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Seleccione una asignación de puerto posterior para cada puerto frontal que " "se vaya a crear." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5621,7 +5630,7 @@ msgstr "" "({frontport_count}) debe coincidir con el número seleccionado de posiciones " "de los puertos traseros ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5631,18 +5640,18 @@ msgstr "" "coincidir con el número seleccionado de posiciones de los puertos traseros " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Miembros" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Posición inicial" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5650,7 +5659,7 @@ msgstr "" "Posición del primer dispositivo miembro. Aumenta en uno por cada miembro " "adicional." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Se debe especificar un puesto para el primer miembro del VC." @@ -6146,6 +6155,7 @@ msgstr "VLAN etiquetadas" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "SVLAN Q-in-Q" @@ -7393,8 +7403,8 @@ msgid "Power outlets" msgstr "tomas de corriente" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7432,8 +7442,8 @@ msgid "Module Bay" msgstr "Bahía de módulos" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7469,7 +7479,7 @@ msgstr "Sorteo asignado (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "Direcciones IP" @@ -7480,7 +7490,7 @@ msgid "FHRP Groups" msgstr "Grupos FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7567,8 +7577,8 @@ msgstr "Altura en U" msgid "Instances" msgstr "Instancias" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7578,8 +7588,8 @@ msgstr "Instancias" msgid "Console Ports" msgstr "Puertos de consola" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7589,8 +7599,8 @@ msgstr "Puertos de consola" msgid "Console Server Ports" msgstr "Puertos de servidor de consola" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7600,8 +7610,8 @@ msgstr "Puertos de servidor de consola" msgid "Power Ports" msgstr "Puertos de alimentación" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7611,8 +7621,8 @@ msgstr "Puertos de alimentación" msgid "Power Outlets" msgstr "Tomas de corriente" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7621,8 +7631,8 @@ msgstr "Tomas de corriente" msgid "Front Ports" msgstr "Puertos frontales" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7632,16 +7642,16 @@ msgstr "Puertos frontales" msgid "Rear Ports" msgstr "Puertos traseros" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Bahías de dispositivos" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7710,64 +7720,64 @@ msgstr "Grupos de VLAN" msgid "Test case must set peer_termination_type" msgstr "El caso de prueba debe establecer peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Desconectado {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Reservaciones" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Dispositivos no rakeados" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Contexto de configuración" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Configuración de renderizado" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Máquinas virtuales" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Dispositivo instalado {device} en la bahía {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Dispositivo eliminado {device} desde la bahía {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Niños" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Miembro agregado {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" "No se puede eliminar el dispositivo maestro {device} desde el chasis " "virtual." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Eliminado {device} desde un chasis virtual {chassis}" @@ -11751,7 +11761,7 @@ msgstr "Funciones de los artículos de inventario" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "Direcciones MAC" @@ -12272,7 +12282,7 @@ msgstr "Valor" msgid "Dummy Plugin" msgstr "Plugin ficticio" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12281,24 +12291,24 @@ msgstr "" "Se ha producido un error al procesar la plantilla de exportación " "seleccionada ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Fila {i}: Objeto con ID {id} no existe" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "No {object_type} fueron seleccionados." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Renombrado {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Eliminado {count} {object_type}" @@ -12327,7 +12337,7 @@ msgstr "Datos sincronizados para {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Sincronizado {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} debe implementar get_children ()" @@ -12604,32 +12614,36 @@ msgstr "Motivo NetBox" msgid "NetBox Logo" msgstr "Logotipo de NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Documentos" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "API DE DESCANSO" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Documentación de la API REST" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "API de GraphQL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Soporte de NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Código fuente" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Comunidad" @@ -13644,7 +13658,7 @@ msgid "PoE Type" msgstr "Tipo de PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Traducción de VLAN" @@ -13697,12 +13711,12 @@ msgstr "Sin interfaces de miembros" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Agregar dirección IP" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Agregar dirección MAC" @@ -16108,7 +16122,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "App_label/model_name desconocido para {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Dirección IP no válida establecida para {header}: {ip}" diff --git a/netbox/translations/fr/LC_MESSAGES/django.po b/netbox/translations/fr/LC_MESSAGES/django.po index 3006f723b..1bd70887f 100644 --- a/netbox/translations/fr/LC_MESSAGES/django.po +++ b/netbox/translations/fr/LC_MESSAGES/django.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: French (https://app.transifex.com/netbox-community/teams/178115/fr/)\n" @@ -75,26 +75,26 @@ msgstr "Dernière utilisation" msgid "Allowed IPs" msgstr "IP autorisées" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Connecté en tant que {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Vous êtes déconnecté." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Vos préférences ont été mises à jour." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Les informations d'identification utilisateur authentifiées par LDAP ne " "peuvent pas être modifiées dans NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Votre mot de passe a été modifié avec succès." @@ -142,7 +142,7 @@ msgstr "Mis hors service" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primaire" @@ -239,7 +239,7 @@ msgstr "Groupe de sites (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -1002,7 +1002,7 @@ msgstr "Attributs" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1101,7 +1101,7 @@ msgstr "Réseau de fournisseurs" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1221,9 +1221,9 @@ msgstr "Rôle opérationnel" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1325,7 +1325,7 @@ msgstr "Contacts" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1348,7 +1348,7 @@ msgstr "Région" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1720,7 +1720,7 @@ msgstr "terminaisons de circuits virtuels" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1976,9 +1976,9 @@ msgstr "Terminaisons" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2153,7 +2153,7 @@ msgstr "Hebdo" msgid "30 days" msgstr "30 jours" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Mis à jour" @@ -2184,7 +2184,7 @@ msgstr "Arrêté" msgid "Cancelled" msgstr "Annulé" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2826,49 +2826,49 @@ msgstr "IDENTIFIANT" msgid "Interval" msgstr "Intervalle" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Version" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Dernière mise à jour" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Version minimale de NetBox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Version maximale de NetBox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Aucune donnée de plug-in trouvée" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Auteur" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Installé" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certifié" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Publié" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Version installée" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Dernière version" @@ -3105,8 +3105,8 @@ msgstr "De l'arrière vers l'avant" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3235,7 +3235,7 @@ msgstr "Virtuel" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3246,7 +3246,7 @@ msgid "Virtual interfaces" msgstr "Interfaces virtuelles" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3633,7 +3633,7 @@ msgstr "Est en pleine profondeur" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3782,7 +3782,7 @@ msgstr "VID attribué" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3803,7 +3803,7 @@ msgstr "VID attribué" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3841,7 +3841,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Politique de traduction VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3879,7 +3879,7 @@ msgstr "Interface LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "Adresse MAC" @@ -3887,14 +3887,14 @@ msgstr "Adresse MAC" msgid "Primary MAC address (ID)" msgstr "Adresse MAC principale (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Adresse MAC principale" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Contexte du périphérique virtuel" @@ -3971,8 +3971,8 @@ msgstr "Étiquettes" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4019,8 +4019,8 @@ msgstr "Fuseau horaire" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4183,7 +4183,7 @@ msgstr "Flux d'air" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4199,7 +4199,7 @@ msgstr "Baie" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Matériel" @@ -4223,15 +4223,24 @@ msgid "Exclude from utilization" msgstr "Exclure de l'utilisation" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Type d'appareil" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4417,8 +4426,8 @@ msgid "Allocated power draw (watts)" msgstr "Consommation électrique allouée (watts)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "port d'alimentation" @@ -4452,7 +4461,7 @@ msgid "Wireless role" msgstr "Rôle sans fil" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4471,7 +4480,7 @@ msgstr "Modules" msgid "LAG" msgstr "DÉCALAGE" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Contextes des appareils virtuels" @@ -4499,21 +4508,21 @@ msgstr "Vitesse" msgid "Mode" msgstr "Mode" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "groupe VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN non étiqueté" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4528,16 +4537,16 @@ msgstr "Ajouter des VLANs étiquetés" msgid "Remove tagged VLANs" msgstr "Retirer des VLANs étiquetés" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Service VLAN Q-in-Q" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Groupe LAN sans fil" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4545,29 +4554,29 @@ msgid "Wireless LANs" msgstr "Réseaux locaux sans fil" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Adressage" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Fonctionnement" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4575,7 +4584,7 @@ msgid "Related Interfaces" msgstr "Interfaces associées" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4838,13 +4847,13 @@ msgstr "Port d'alimentation local qui alimente cette prise" msgid "Electrical phase (for three-phase circuits)" msgstr "Phase électrique (pour circuits triphasés)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Interface pour les parents" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4909,8 +4918,8 @@ msgstr "Rôle sans fil (AP/station)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} n'est pas attribué à l'appareil {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Port arrière" @@ -5093,7 +5102,7 @@ msgstr "Type d'alimentation (AC/DC)" msgid "Single or three-phase" msgstr "Monophasé ou triphasé" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5104,7 +5113,7 @@ msgstr "IPv4 principal" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Adresse IPv4 avec masque, par exemple 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5274,7 +5283,7 @@ msgstr "Type" msgid "Mgmt only" msgstr "Gestion uniquement" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5421,7 +5430,7 @@ msgstr "Remplir automatiquement les composants associés à ce type de module" msgid "Characteristics" msgstr "Caractéristiques" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5436,35 +5445,35 @@ msgstr "" "{module}, s'il est présent, sera automatiquement remplacé par " "la valeur de position lors de la création d'un nouveau module." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Modèle de port de console" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Modèle de port de serveur de console" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Modèle de port avant" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Modèle d'interface" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Modèle de prise de courant" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Modèle de port d'alimentation" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Modèle de port arrière" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5472,14 +5481,14 @@ msgstr "Modèle de port arrière" msgid "Console Port" msgstr "Port de console" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Port du serveur de consoles" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5490,7 +5499,7 @@ msgstr "Port du serveur de consoles" msgid "Front Port" msgstr "Port avant" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5503,40 +5512,40 @@ msgstr "Port avant" msgid "Rear Port" msgstr "Port arrière" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Port d'alimentation" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Prise de courant" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Affectation des composants" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Un item d'inventaire ne peut être attribué qu'à un seul composant." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Interface LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtrez les VLAN disponibles pour une attribution par groupe." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Appareil pour enfants" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5544,37 +5553,37 @@ msgstr "" "Les appareils enfants doivent d'abord être créés et affectés au site et à la" " baie de l'appareil parent." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Port de console" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Port du serveur de console" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Port avant" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "prise de courant" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Article d'inventaire" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Rôle de l'article d'inventaire" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Interface de machine virtuelle" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5592,13 +5601,13 @@ msgstr "Interface de machine virtuelle" msgid "Virtual Machine" msgstr "Machine virtuelle" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Une adresse MAC ne peut être attribuée qu'à un seul objet." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5616,16 +5625,16 @@ msgstr "" "sont attendus." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Ports arrière" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "Associer un port arrière à chaque port avant en cours de création." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5635,7 +5644,7 @@ msgstr "" "correspondre au nombre sélectionné de positions des ports arrière " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5644,18 +5653,18 @@ msgstr "" "Le nombre de ports frontaux à créer ({frontport_count}) doit correspondre au" " nombre sélectionné de positions des ports arrière ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Membres" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Position initiale" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5663,7 +5672,7 @@ msgstr "" "Position du premier dispositif membre. Augmente d'une unité pour chaque " "membre supplémentaire." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Une position doit être spécifiée pour le premier membre du VC." @@ -6159,6 +6168,7 @@ msgstr "VLAN étiquetés" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "SVLAN Q-in-Q" @@ -7406,8 +7416,8 @@ msgid "Power outlets" msgstr "Prises de courant" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7445,8 +7455,8 @@ msgid "Module Bay" msgstr "Module Bay" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7482,7 +7492,7 @@ msgstr "Tirage alloué (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "Adresses IP" @@ -7493,7 +7503,7 @@ msgid "FHRP Groups" msgstr "Groupes FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7580,8 +7590,8 @@ msgstr "Hauteur en U" msgid "Instances" msgstr "Instances" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7591,8 +7601,8 @@ msgstr "Instances" msgid "Console Ports" msgstr "Ports de console" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7602,8 +7612,8 @@ msgstr "Ports de console" msgid "Console Server Ports" msgstr "Ports du serveur de consoles" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7613,8 +7623,8 @@ msgstr "Ports du serveur de consoles" msgid "Power Ports" msgstr "Ports d'alimentation" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7624,8 +7634,8 @@ msgstr "Ports d'alimentation" msgid "Power Outlets" msgstr "Prises de courant" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7634,8 +7644,8 @@ msgstr "Prises de courant" msgid "Front Ports" msgstr "Ports avant" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7645,16 +7655,16 @@ msgstr "Ports avant" msgid "Rear Ports" msgstr "Ports arrière" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Baies pour appareils" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7723,64 +7733,64 @@ msgstr "Groupes VLAN" msgid "Test case must set peer_termination_type" msgstr "Le scénario de test doit définir peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Déconnecté {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Réservations" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Appareils non mis en baie" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Contexte de configuration" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Configuration du rendu" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Machines virtuelles" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Appareil installé {device} dans la baie {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Appareil retiré {device} depuis la baie {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Enfants" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Membre ajouté {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" "Impossible de supprimer le périphérique principal {device} depuis le châssis" " virtuel." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Supprimé {device} depuis un châssis virtuel {chassis}" @@ -11792,7 +11802,7 @@ msgstr "Rôles des articles d'inventaire" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "Adresses MAC" @@ -12316,7 +12326,7 @@ msgstr "Valeur" msgid "Dummy Plugin" msgstr "Plugin Dummy" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12325,24 +12335,24 @@ msgstr "" "Une erreur s'est produite lors de l'affichage du modèle d'exportation " "sélectionné ({template}) : {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Rangée {i}: Objet avec identifiant {id} n'existe pas" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Non {object_type} ont été sélectionnés." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Renommé {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Supprimé {count} {object_type}" @@ -12371,7 +12381,7 @@ msgstr "Données synchronisées pour {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Synchronisé {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} doit implémenter get_children ()" @@ -12649,32 +12659,36 @@ msgstr "Motif NetBox" msgid "NetBox Logo" msgstr "Logo NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Docs" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "API REST" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Documentation de l'API REST" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "API GraphQL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Assistance NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Code source" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Communauté" @@ -13688,7 +13702,7 @@ msgid "PoE Type" msgstr "Type de PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Traduction VLAN" @@ -13741,12 +13755,12 @@ msgstr "Aucune interface membre" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Ajouter une adresse IP" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Ajouter une adresse MAC" @@ -16151,7 +16165,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "App_label/model_name inconnu pour {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Adresse IP non valide définie pour {header}: {ip}" diff --git a/netbox/translations/it/LC_MESSAGES/django.po b/netbox/translations/it/LC_MESSAGES/django.po index 2ebb16492..8cc7335a8 100644 --- a/netbox/translations/it/LC_MESSAGES/django.po +++ b/netbox/translations/it/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Italian (https://app.transifex.com/netbox-community/teams/178115/it/)\n" @@ -67,26 +67,26 @@ msgstr "Ultimo utilizzo" msgid "Allowed IPs" msgstr "IP consentiti" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Effettuato l'accesso come {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Ti sei disconnesso." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Le tue preferenze sono state aggiornate." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Le credenziali utente autenticate con LDAP non possono essere modificate " "all'interno di NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "La tua password è stata cambiata con successo." @@ -134,7 +134,7 @@ msgstr "Dismesso" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primaria" @@ -231,7 +231,7 @@ msgstr "Gruppo del sito (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -994,7 +994,7 @@ msgstr "Attributi" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1093,7 +1093,7 @@ msgstr "Provider network" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1213,9 +1213,9 @@ msgstr "Ruolo operativo" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1317,7 +1317,7 @@ msgstr "Contatti" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1340,7 +1340,7 @@ msgstr "Regione" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1712,7 +1712,7 @@ msgstr "terminazioni di circuiti virtuali" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1968,9 +1968,9 @@ msgstr "Terminazioni" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2146,7 +2146,7 @@ msgstr "Settimanale" msgid "30 days" msgstr "30 giorni" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Aggiornato" @@ -2177,7 +2177,7 @@ msgstr "Fermato" msgid "Cancelled" msgstr "Annullato" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2815,49 +2815,49 @@ msgstr "ID" msgid "Interval" msgstr "Intervallo" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Versione" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Ultimo aggiornamento" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Versione minima di NetBox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Versione massima di NetBox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Nessun dato del plugin trovato" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Autore" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Installato" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certificato" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Pubblicato" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Versione installata" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Ultima versione" @@ -3094,8 +3094,8 @@ msgstr "Posteriore/anteriore" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3224,7 +3224,7 @@ msgstr "Virtuale" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3235,7 +3235,7 @@ msgid "Virtual interfaces" msgstr "Interfacce virtuali" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3622,7 +3622,7 @@ msgstr "È a piena profondità" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3771,7 +3771,7 @@ msgstr "VID assegnato" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3792,7 +3792,7 @@ msgstr "VID assegnato" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3830,7 +3830,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Politica di traduzione VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3868,7 +3868,7 @@ msgstr "Interfaccia LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "Indirizzo MAC" @@ -3876,14 +3876,14 @@ msgstr "Indirizzo MAC" msgid "Primary MAC address (ID)" msgstr "Indirizzo MAC (ID) primario" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Indirizzo MAC primario" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Contesto del dispositivo virtuale" @@ -3960,8 +3960,8 @@ msgstr "Etichette" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4008,8 +4008,8 @@ msgstr "Fuso orario" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4172,7 +4172,7 @@ msgstr "Flusso d'aria" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4188,7 +4188,7 @@ msgstr "cremagliera" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4212,15 +4212,24 @@ msgid "Exclude from utilization" msgstr "Escludi dall'utilizzo" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Tipo di dispositivo" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4406,8 +4415,8 @@ msgid "Allocated power draw (watts)" msgstr "Potenza assorbita allocata (watt)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Porta di alimentazione" @@ -4441,7 +4450,7 @@ msgid "Wireless role" msgstr "Ruolo wireless" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4460,7 +4469,7 @@ msgstr "Modulo" msgid "LAG" msgstr "RITARDO" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Contesti dei dispositivi virtuali" @@ -4488,21 +4497,21 @@ msgstr "Velocità" msgid "Mode" msgstr "modalità" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Gruppo VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN senza tag" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4517,16 +4526,16 @@ msgstr "Aggiungi VLAN con tag" msgid "Remove tagged VLANs" msgstr "Rimuovi le VLAN contrassegnate" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "VLAN di servizio Q-in-Q" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Gruppo LAN wireless" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4534,29 +4543,29 @@ msgid "Wireless LANs" msgstr "LAN wireless" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Indirizzamento" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operazione" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4564,7 +4573,7 @@ msgid "Related Interfaces" msgstr "Interfacce correlate" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4830,13 +4839,13 @@ msgstr "Porta di alimentazione locale che alimenta questa presa" msgid "Electrical phase (for three-phase circuits)" msgstr "Fase elettrica (per circuiti trifase)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Interfaccia principale" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4901,8 +4910,8 @@ msgstr "Ruolo wireless (AP/stazione)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} non è assegnato al dispositivo {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Porta posteriore" @@ -5086,7 +5095,7 @@ msgstr "Tipo di alimentazione (AC/DC)" msgid "Single or three-phase" msgstr "Monofase o trifase" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5097,7 +5106,7 @@ msgstr "IPv4 primario" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Indirizzo IPv4 con maschera, ad esempio 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5265,7 +5274,7 @@ msgstr "Gentile" msgid "Mgmt only" msgstr "Solo gestione" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5415,7 +5424,7 @@ msgstr "" msgid "Characteristics" msgstr "Caratteristiche" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5430,35 +5439,35 @@ msgstr "" "{module}, se presente, verrà automaticamente sostituito con il " "valore della posizione durante la creazione di un nuovo modulo." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Modello di porta console" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Modello di porta del server console" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Modello di porta anteriore" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Modello di interfaccia" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Modello di presa di corrente" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Modello di porta di alimentazione" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Modello di porta posteriore" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5466,14 +5475,14 @@ msgstr "Modello di porta posteriore" msgid "Console Port" msgstr "Porta console" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Porta Console Server" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5484,7 +5493,7 @@ msgstr "Porta Console Server" msgid "Front Port" msgstr "Porta anteriore" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5497,40 +5506,40 @@ msgstr "Porta anteriore" msgid "Rear Port" msgstr "Porta posteriore" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Porta di alimentazione" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Presa di corrente" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Assegnazione dei componenti" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Un InventoryItem può essere assegnato solo a un singolo componente." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Interfaccia LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtra le VLAN disponibili per l'assegnazione per gruppo." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Dispositivo per bambini" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5538,37 +5547,37 @@ msgstr "" "I dispositivi secondari devono prima essere creati e assegnati al sito e al " "rack del dispositivo principale." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Porta console" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Porta console server" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Porta anteriore" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Presa di corrente" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Articolo di inventario" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Ruolo dell'articolo di inventario" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Interfaccia VM" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5586,13 +5595,13 @@ msgstr "Interfaccia VM" msgid "Virtual Machine" msgstr "Macchina virtuale" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Un indirizzo MAC può essere assegnato a un solo oggetto." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5610,18 +5619,18 @@ msgstr "" "attesi." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Porte posteriori" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Seleziona un'assegnazione della porta posteriore per ogni porta anteriore da" " creare." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5631,7 +5640,7 @@ msgstr "" "corrispondere al numero selezionato di posizioni delle porte posteriori " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5641,18 +5650,18 @@ msgstr "" " al numero selezionato di posizioni delle porte posteriori " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Membri" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Posizione iniziale" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5660,7 +5669,7 @@ msgstr "" "Posizione del primo dispositivo membro. Aumenta di uno per ogni membro " "aggiuntivo." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "È necessario specificare una posizione per il primo membro VC." @@ -6163,6 +6172,7 @@ msgstr "VLAN contrassegnate" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "SVLAN Q-in-Q" @@ -7419,8 +7429,8 @@ msgid "Power outlets" msgstr "Prese di corrente" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7458,8 +7468,8 @@ msgid "Module Bay" msgstr "Modulo Bay" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7495,7 +7505,7 @@ msgstr "Pareggio assegnato (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "Indirizzi IP" @@ -7506,7 +7516,7 @@ msgid "FHRP Groups" msgstr "Gruppi FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7593,8 +7603,8 @@ msgstr "Altezza U" msgid "Instances" msgstr "Istanze" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7604,8 +7614,8 @@ msgstr "Istanze" msgid "Console Ports" msgstr "Porte console" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7615,8 +7625,8 @@ msgstr "Porte console" msgid "Console Server Ports" msgstr "Porte Console Server" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7626,8 +7636,8 @@ msgstr "Porte Console Server" msgid "Power Ports" msgstr "Porte di alimentazione" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7637,8 +7647,8 @@ msgstr "Porte di alimentazione" msgid "Power Outlets" msgstr "Prese di corrente" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7647,8 +7657,8 @@ msgstr "Prese di corrente" msgid "Front Ports" msgstr "Porte anteriori" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7658,16 +7668,16 @@ msgstr "Porte anteriori" msgid "Rear Ports" msgstr "Porte posteriori" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Alloggiamenti per dispositivi" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7736,63 +7746,63 @@ msgstr "Gruppi VLAN" msgid "Test case must set peer_termination_type" msgstr "Il test case deve impostare peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Disconnesso {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Prenotazioni" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Dispositivi non montati su rack" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Contesto di configurazione" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Configurazione del rendering" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Macchine virtuali" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Dispositivo installato {device} nella baia {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Dispositivo rimosso {device} dalla baia {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Bambini" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Membro aggiunto {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" "Impossibile rimuovere il dispositivo master {device} dallo chassis virtuale." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Rimosso {device} da chassis virtuale {chassis}" @@ -11782,7 +11792,7 @@ msgstr "Ruoli degli articoli di inventario" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "Indirizzi MAC" @@ -12307,7 +12317,7 @@ msgstr "Valore" msgid "Dummy Plugin" msgstr "Plugin fittizio" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12316,24 +12326,24 @@ msgstr "" "Si è verificato un errore durante il rendering del modello di esportazione " "selezionato ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Fila {i}: Oggetto con ID {id} non esiste" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "No {object_type} sono stati selezionati." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Rinominato {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Eliminato {count} {object_type}" @@ -12360,7 +12370,7 @@ msgstr "Dati sincronizzati per {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Sincronizzato {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} deve implementare get_children ()" @@ -12636,32 +12646,36 @@ msgstr "Motivo NetBox" msgid "NetBox Logo" msgstr "Logo NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Documenti" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "API REST" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Documentazione API REST" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "API GraphQL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Supporto NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Codice sorgente" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Comunità" @@ -13674,7 +13688,7 @@ msgid "PoE Type" msgstr "Tipo PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Traduzione VLAN" @@ -13727,12 +13741,12 @@ msgstr "Nessuna interfaccia membro" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Aggiungi indirizzo IP" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Aggiungi indirizzo MAC" @@ -16133,7 +16147,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "app_label/model_name sconosciuto per {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Indirizzo IP non valido impostato per {header}: {ip}" diff --git a/netbox/translations/ja/LC_MESSAGES/django.po b/netbox/translations/ja/LC_MESSAGES/django.po index c50a77c64..494cb57b8 100644 --- a/netbox/translations/ja/LC_MESSAGES/django.po +++ b/netbox/translations/ja/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Japanese (https://app.transifex.com/netbox-community/teams/178115/ja/)\n" @@ -66,24 +66,24 @@ msgstr "最終使用日" msgid "Allowed IPs" msgstr "許可された IP" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "{user}としてログイン 。" -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "ログアウトしました。" -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "設定が更新されました。" -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "LDAP認証されたユーザー資格情報は、NetBox内で変更することはできません。" -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "パスワードは正常に変更されました。" @@ -131,7 +131,7 @@ msgstr "廃止" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "プライマリ" @@ -228,7 +228,7 @@ msgstr "サイトグループ (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -991,7 +991,7 @@ msgstr "属性" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1090,7 +1090,7 @@ msgstr "プロバイダネットワーク" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1210,9 +1210,9 @@ msgstr "運用上のロール" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1314,7 +1314,7 @@ msgstr "連絡先" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1337,7 +1337,7 @@ msgstr "リージョン" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1708,7 +1708,7 @@ msgstr "仮想回線終端" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1964,9 +1964,9 @@ msgstr "終端" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2140,7 +2140,7 @@ msgstr "毎週" msgid "30 days" msgstr "30 日毎" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "更新" @@ -2171,7 +2171,7 @@ msgstr "停止済" msgid "Cancelled" msgstr "キャンセル済" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2796,49 +2796,49 @@ msgstr "ID" msgid "Interval" msgstr "間隔" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "バージョン" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "最終更新日" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "NetBox の最小バージョン" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "NetBoxの最大バージョン" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "プラグインデータが見つかりません" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "著者" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "インストール済" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "認定済" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "公開済" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "インストール済バージョン" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "最新バージョン" @@ -3075,8 +3075,8 @@ msgstr "背面から前面" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3205,7 +3205,7 @@ msgstr "仮想" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3216,7 +3216,7 @@ msgid "Virtual interfaces" msgstr "仮想インタフェース" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3603,7 +3603,7 @@ msgstr "奥行きをすべて使う" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3752,7 +3752,7 @@ msgstr "割当 VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3773,7 +3773,7 @@ msgstr "割当 VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3811,7 +3811,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "VLAN 変換ポリシー (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3849,7 +3849,7 @@ msgstr "LAG インタフェース (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC アドレス" @@ -3857,14 +3857,14 @@ msgstr "MAC アドレス" msgid "Primary MAC address (ID)" msgstr "プライマリ MAC アドレス (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "プライマリ MAC アドレス" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "仮想デバイスコンテキスト" @@ -3941,8 +3941,8 @@ msgstr "タグ" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3987,8 +3987,8 @@ msgstr "タイムゾーン" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4151,7 +4151,7 @@ msgstr "エアフロー" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4167,7 +4167,7 @@ msgstr "ラック" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "ハードウェア" @@ -4191,15 +4191,24 @@ msgid "Exclude from utilization" msgstr "ラック利用率に含めない" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "デバイスタイプ" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4385,8 +4394,8 @@ msgid "Allocated power draw (watts)" msgstr "割当消費電力 (ワット)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "電源ポート" @@ -4420,7 +4429,7 @@ msgid "Wireless role" msgstr "無線ロール" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4439,7 +4448,7 @@ msgstr "モジュール" msgid "LAG" msgstr "LAG" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "仮想デバイスコンテキスト" @@ -4467,21 +4476,21 @@ msgstr "速度" msgid "Mode" msgstr "モード" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "VLAN グループ" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "タグなし VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4496,16 +4505,16 @@ msgstr "タグ付 VLAN の追加" msgid "Remove tagged VLANs" msgstr "タグ付 VLAN の削除" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Q-in-Q サービス VLAN" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "無線 LAN グループ" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4513,29 +4522,29 @@ msgid "Wireless LANs" msgstr "無線 LAN" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "アドレス" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "オペレーション" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4543,7 +4552,7 @@ msgid "Related Interfaces" msgstr "関連インタフェース" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4801,13 +4810,13 @@ msgstr "このコンセントに給電する電源ポート" msgid "Electrical phase (for three-phase circuits)" msgstr "電気位相 (三相回路用)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "親インタフェース" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4871,8 +4880,8 @@ msgstr "無線ロール (AP/ステーション)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} デバイスには割り当てられていません {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "背面ポート" @@ -5049,7 +5058,7 @@ msgstr "電源タイプ (AC/DC)" msgid "Single or three-phase" msgstr "単相または三相" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5060,7 +5069,7 @@ msgstr "プライマリ IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "マスク付きの IPv4 アドレス (例:1.2.3.4/24)" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5223,7 +5232,7 @@ msgstr "種類" msgid "Mgmt only" msgstr "管理のみ" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5365,7 +5374,7 @@ msgstr "このモジュールタイプに関連する構成要素を自動的に msgid "Characteristics" msgstr "特性" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5378,35 +5387,35 @@ msgstr "" "1[ge,xe]-0/0/[0-9]1)。トークン " "{module}が存在する場合、新しいモジュールを作成する際に、自動的に位置の値に置き換えられます。" -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "コンソールポートテンプレート" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "コンソールサーバポートテンプレート" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "前面ポートテンプレート" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "インタフェーステンプレート" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "電源コンセントテンプレート" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "電源ポートテンプレート" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "背面ポートテンプレート" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5414,14 +5423,14 @@ msgstr "背面ポートテンプレート" msgid "Console Port" msgstr "コンソールポート" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "コンソールサーバポート" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5432,7 +5441,7 @@ msgstr "コンソールサーバポート" msgid "Front Port" msgstr "前面ポート" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5445,76 +5454,76 @@ msgstr "前面ポート" msgid "Rear Port" msgstr "背面ポート" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "電源ポート" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "電源コンセント" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "構成要素割り当て" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "在庫品目は1つの構成要素にのみ割り当てることができます。" -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "LAG インタフェース" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "割り当て可能な VLAN をグループ別にフィルタリングします。" -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "子デバイス" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." msgstr "まず子デバイスを作成し、親デバイスのサイトとラックに割り当てる必要があります。" -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "コンソールポート" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "コンソールサーバポート" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "前面ポート" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "電源コンセント" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "在庫品目" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "在庫品目ロール" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "VM インターフェイス" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5532,13 +5541,13 @@ msgstr "VM インターフェイス" msgid "Virtual Machine" msgstr "仮想マシン" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "MAC アドレスは 1 つのオブジェクトにのみ割り当てることができます。" #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5552,16 +5561,16 @@ msgid "" msgstr "パターンは {value_count} 個の値を示す範囲を指定しますが、 {pattern_count} 個の値が必要です。" #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "背面ポート" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "前面ポートごとに背面ポート 1 つ割り当てます。" -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5570,7 +5579,7 @@ msgstr "" "前面ポートテンプレートの数 ({frontport_count}) " "は選択した背面ポートの数({rearport_count})と一致する必要があります。" -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5578,24 +5587,24 @@ msgid "" msgstr "" "前面ポートの数 ({frontport_count}) は選択した背面ポートの数 ({rearport_count}) と一致する必要があります。" -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "メンバー" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "初期ポジション" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." msgstr "最初のメンバーのポジション。メンバーが増えるごとに 1 ずつ増えます。" -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "最初の VC メンバーのポジションを指定する必要があります。" @@ -6058,6 +6067,7 @@ msgstr "タグ付き VLAN" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q SVLAN" @@ -7226,8 +7236,8 @@ msgid "Power outlets" msgstr "電源コンセント" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7265,8 +7275,8 @@ msgid "Module Bay" msgstr "モジュールベイ" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7302,7 +7312,7 @@ msgstr "割当電力 (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP アドレス" @@ -7313,7 +7323,7 @@ msgid "FHRP Groups" msgstr "FHRP グループ" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7400,8 +7410,8 @@ msgstr "ユニット数" msgid "Instances" msgstr "インスタンス" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7411,8 +7421,8 @@ msgstr "インスタンス" msgid "Console Ports" msgstr "コンソールポート" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7422,8 +7432,8 @@ msgstr "コンソールポート" msgid "Console Server Ports" msgstr "コンソールサーバポート" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7433,8 +7443,8 @@ msgstr "コンソールサーバポート" msgid "Power Ports" msgstr "電源ポート" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7444,8 +7454,8 @@ msgstr "電源ポート" msgid "Power Outlets" msgstr "電源コンセント" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7454,8 +7464,8 @@ msgstr "電源コンセント" msgid "Front Ports" msgstr "前面ポート" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7465,16 +7475,16 @@ msgstr "前面ポート" msgid "Rear Ports" msgstr "背面ポート" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "デバイスベイ" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7543,62 +7553,62 @@ msgstr "VLAN グループ" msgid "Test case must set peer_termination_type" msgstr "テストケースは peer_termination_type を設定する必要があります" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "切断されました {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "予約" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "ラック搭載でないデバイス" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "コンフィグコンテキスト" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "レンダーコンフィグ" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "仮想マシン" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "インストール済みデバイス {device} イン・ベイ {device_bay}。" -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "削除されたデバイス {device} ベイから {device_bay}。" -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "子ども" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "メンバー追加 {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "マスターデバイスを削除できません {device} バーチャルシャーシから。" -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "削除済み {device} バーチャルシャーシから {chassis}" @@ -11428,7 +11438,7 @@ msgstr "在庫品目のロール" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC アドレス" @@ -11945,31 +11955,31 @@ msgstr "値" msgid "Dummy Plugin" msgstr "ダミープラグイン" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " "{error}" msgstr "選択したエクスポートテンプレートをレンダリング中にエラーが発生しました ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "行 {i}: ID {id}のオブジェクトは存在しません" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "いいえ {object_type} が選ばれました。" -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "名前が変更されました {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "削除済み {count} {object_type}" @@ -11996,7 +12006,7 @@ msgstr "の同期データ {object_type} {object}。" msgid "Synced {count} {object_type}" msgstr "同期済み {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} はget_children () を実装する必要があります" @@ -12269,32 +12279,36 @@ msgstr "ネットボックスモチーフ" msgid "NetBox Logo" msgstr "NetBoxロゴ" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "ドキュメント" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "REST API ドキュメント" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "ネットボックスラボサポート" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "ソースコード" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "コミュニティ" @@ -13302,7 +13316,7 @@ msgid "PoE Type" msgstr "PoE タイプ" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "VLAN トランスレーション" @@ -13355,12 +13369,12 @@ msgstr "メンバーインタフェースなし" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "IP アドレスを追加" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "MAC アドレスを追加" @@ -15677,7 +15691,7 @@ msgstr "権限名が無効です: {name}。次の形式である必要があり msgid "Unknown app_label/model_name for {name}" msgstr "のアプリケーションラベル/モデル名が不明です {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "に設定された IP アドレスが無効です {header}: {ip}" diff --git a/netbox/translations/nl/LC_MESSAGES/django.po b/netbox/translations/nl/LC_MESSAGES/django.po index 3f8b02340..783d19a0c 100644 --- a/netbox/translations/nl/LC_MESSAGES/django.po +++ b/netbox/translations/nl/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Dutch (https://app.transifex.com/netbox-community/teams/178115/nl/)\n" @@ -69,26 +69,26 @@ msgstr "Laatst gebruikt" msgid "Allowed IPs" msgstr "Toegestane IP-adressen" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Ingelogd als {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Je bent uitgelogd." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Je voorkeuren zijn bijgewerkt." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "LDAP-geverifieerde gebruikersgegevens kunnen niet worden gewijzigd in " "NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Je wachtwoord is succesvol gewijzigd." @@ -136,7 +136,7 @@ msgstr "Buiten gebruik" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primair" @@ -233,7 +233,7 @@ msgstr "Sitegroep (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -996,7 +996,7 @@ msgstr "Attributen" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1095,7 +1095,7 @@ msgstr "Netwerkprovider" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1215,9 +1215,9 @@ msgstr "Operationele rol" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1319,7 +1319,7 @@ msgstr "Contacten" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1342,7 +1342,7 @@ msgstr "Regio" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1714,7 +1714,7 @@ msgstr "beëindigingen van virtuele circuits" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1970,9 +1970,9 @@ msgstr "Beëindigingen" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2148,7 +2148,7 @@ msgstr "Wekelijks" msgid "30 days" msgstr "30 dagen" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Bijgewerkt" @@ -2179,7 +2179,7 @@ msgstr "Gestopt" msgid "Cancelled" msgstr "Geannuleerd" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2817,49 +2817,49 @@ msgstr "ID" msgid "Interval" msgstr "Interval" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Versie" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Laatst bijgewerkt" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Minimale NetBox-versie" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Maximale NetBox-versie" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Geen plugin-gegevens gevonden" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Auteur" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Geïnstalleerd" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Gecertificeerd" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Gepubliceerd" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Geïnstalleerde versie" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Laatste versie" @@ -3096,8 +3096,8 @@ msgstr "Van achter naar voren" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3226,7 +3226,7 @@ msgstr "Virtueel" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3237,7 +3237,7 @@ msgid "Virtual interfaces" msgstr "Virtuele interfaces" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3624,7 +3624,7 @@ msgstr "Is volledige diepte" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3773,7 +3773,7 @@ msgstr "Toegewezen VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3794,7 +3794,7 @@ msgstr "Toegewezen VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3832,7 +3832,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "VLAN-vertaalbeleid (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3870,7 +3870,7 @@ msgstr "LAG-interface (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC-adres" @@ -3878,14 +3878,14 @@ msgstr "MAC-adres" msgid "Primary MAC address (ID)" msgstr "Primair MAC-adres (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Primair MAC-adres" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Context van het virtuele apparaat" @@ -3962,8 +3962,8 @@ msgstr "Labels" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4010,8 +4010,8 @@ msgstr "Tijdzone" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4174,7 +4174,7 @@ msgstr "Luchtstroom" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4190,7 +4190,7 @@ msgstr "Rek" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4214,15 +4214,24 @@ msgid "Exclude from utilization" msgstr "Uitsluiten van gebruik" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Soort apparaat" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4408,8 +4417,8 @@ msgid "Allocated power draw (watts)" msgstr "Toegewezen stroomverbruik (watt)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Voedingspoort" @@ -4443,7 +4452,7 @@ msgid "Wireless role" msgstr "Draadloze rol" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4462,7 +4471,7 @@ msgstr "Module" msgid "LAG" msgstr "LAG" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Contexten van virtuele apparaten" @@ -4490,21 +4499,21 @@ msgstr "Snelheid" msgid "Mode" msgstr "Modus" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "VLAN-groep" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN zonder label" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4519,16 +4528,16 @@ msgstr "Getagde VLAN's toevoegen" msgid "Remove tagged VLANs" msgstr "Getagde VLAN's verwijderen" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "VLAN voor Q-in-Q-service" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Draadloze LAN-groep" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4536,29 +4545,29 @@ msgid "Wireless LANs" msgstr "Draadloze LAN's" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Addressing" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operatie" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4566,7 +4575,7 @@ msgid "Related Interfaces" msgstr "Gerelateerde interfaces" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4831,13 +4840,13 @@ msgstr "Lokale voedingspoort die dit stopcontact voedt" msgid "Electrical phase (for three-phase circuits)" msgstr "Elektrische fase (voor driefasige circuits)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Interface voor ouders" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4903,8 +4912,8 @@ msgstr "Draadloze rol (AP/station)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} is niet toegewezen aan het apparaat {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Poort aan de achterkant" @@ -5087,7 +5096,7 @@ msgstr "Soort voeding (AC/DC)" msgid "Single or three-phase" msgstr "Enkel- of driefasig" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5098,7 +5107,7 @@ msgstr "Primaire IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "IPv4-adres met masker, bijvoorbeeld 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5266,7 +5275,7 @@ msgstr "Soort" msgid "Mgmt only" msgstr "Alleen voor beheer" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5416,7 +5425,7 @@ msgstr "" msgid "Characteristics" msgstr "Kenmerken" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5431,35 +5440,35 @@ msgstr "" "indien aanwezig, wordt automatisch vervangen door de positiewaarde bij het " "aanmaken van een nieuwe module." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Sjabloon voor consolepoort" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Poortsjabloon voor consoleserver" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Sjabloon voor de voorpoort" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Interfacesjabloon" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Sjabloon voor stopcontact" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Sjabloon voor voedingspoort" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Sjabloon voor achterpoort" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5467,14 +5476,14 @@ msgstr "Sjabloon voor achterpoort" msgid "Console Port" msgstr "Consolepoort" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Console Server-poort" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5485,7 +5494,7 @@ msgstr "Console Server-poort" msgid "Front Port" msgstr "Poort Voor" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5498,40 +5507,40 @@ msgstr "Poort Voor" msgid "Rear Port" msgstr "Poort achter" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Voedingspoort" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Stopcontact" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Toewijzing van componenten" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Een InventoryItem kan slechts aan één component worden toegewezen." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "LAG-interface" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filter-VLAN's die beschikbaar zijn voor toewijzing per groep." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Apparaat voor kinderen" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5539,37 +5548,37 @@ msgstr "" "Kindapparaten moeten eerst worden aangemaakt en toegewezen aan de site en " "het rack van het ouderapparaat." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Consolepoort" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Console-serverpoort" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Poort voor" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Stopcontact" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Inventarisitem" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Rol van het inventarisitem" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "VM-interface" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5587,13 +5596,13 @@ msgstr "VM-interface" msgid "Virtual Machine" msgstr "Virtuele machine" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Een MAC-adres kan slechts aan één object worden toegewezen." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5611,18 +5620,18 @@ msgstr "" "{pattern_count} worden verwacht." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Poorten achter" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Selecteer één toewijzing van de achterpoort voor elke poort aan de voorkant " "die wordt gemaakt." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5632,7 +5641,7 @@ msgstr "" "moet overeenkomen met het geselecteerde aantal posities aan de achterkant " "van de poort ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5642,18 +5651,18 @@ msgstr "" "overeenkomen met het geselecteerde aantal posities aan de achterkant van de " "poort ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Leden" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Uitgangspositie" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5661,7 +5670,7 @@ msgstr "" "Positie van het apparaat van het eerste lid. Verhoogt met één voor elk extra" " lid." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Voor het eerste VC-lid moet een positie worden gespecificeerd." @@ -6159,6 +6168,7 @@ msgstr "gelabelde VLAN's" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q SVLAN" @@ -7398,8 +7408,8 @@ msgid "Power outlets" msgstr "Stopcontacten" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7437,8 +7447,8 @@ msgid "Module Bay" msgstr "Modulebaai" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7474,7 +7484,7 @@ msgstr "Toegewezen trekking (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP-adressen" @@ -7485,7 +7495,7 @@ msgid "FHRP Groups" msgstr "FHRP-groepen" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7572,8 +7582,8 @@ msgstr "U-hoogte" msgid "Instances" msgstr "Instanties" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7583,8 +7593,8 @@ msgstr "Instanties" msgid "Console Ports" msgstr "Consolepoorten" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7594,8 +7604,8 @@ msgstr "Consolepoorten" msgid "Console Server Ports" msgstr "Serverpoorten voor de console" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7605,8 +7615,8 @@ msgstr "Serverpoorten voor de console" msgid "Power Ports" msgstr "Voedingspoorten" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7616,8 +7626,8 @@ msgstr "Voedingspoorten" msgid "Power Outlets" msgstr "Stopcontacten" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7626,8 +7636,8 @@ msgstr "Stopcontacten" msgid "Front Ports" msgstr "Ports aan de voorkant" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7637,16 +7647,16 @@ msgstr "Ports aan de voorkant" msgid "Rear Ports" msgstr "Poorten achteraan" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Apparaatvakken" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7715,63 +7725,63 @@ msgstr "VLAN-groepen" msgid "Test case must set peer_termination_type" msgstr "De testcase moet peer_termination_type instellen" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Verbinding verbroken {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Reserveringen" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Apparaten zonder rack" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Context van de configuratie" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Render-configuratie" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Virtuele machines" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Geïnstalleerd apparaat {device} in de baai {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Apparaat verwijderd {device} van bay {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Kinderen" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Lid toegevoegd {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" "Kan het masterapparaat niet verwijderen {device} vanaf het virtuele chassis." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Verwijderd {device} vanaf een virtueel chassis {chassis}" @@ -11752,7 +11762,7 @@ msgstr "Rollen van inventarisitems" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC-adressen" @@ -12275,7 +12285,7 @@ msgstr "Waarde" msgid "Dummy Plugin" msgstr "Dummy-plug-in" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12284,24 +12294,24 @@ msgstr "" "Er is een fout opgetreden bij het weergeven van de geselecteerde " "exportsjabloon ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Rij {i}: Object met ID {id} bestaat niet" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Geen {object_type} zijn geselecteerd." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Hernoemd {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Verwijderd {count} {object_type}" @@ -12329,7 +12339,7 @@ msgstr "Gesynchroniseerde gegevens voor {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Gesynchroniseerd {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} moet get_children () implementeren" @@ -12605,32 +12615,36 @@ msgstr "NetBox-motief" msgid "NetBox Logo" msgstr "NetBox-logo" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Documenten" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "REST API-documentatie" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL-API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Ondersteuning voor NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Broncode" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Gemeenschap" @@ -13645,7 +13659,7 @@ msgid "PoE Type" msgstr "PoE-type" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "VLAN-vertaling" @@ -13698,12 +13712,12 @@ msgstr "Geen interfaces voor leden" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "IP-adres toevoegen" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "MAC-adres toevoegen" @@ -16105,7 +16119,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Onbekende app_label/model_name voor {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Ongeldig IP-adres ingesteld voor {header}: {ip}" diff --git a/netbox/translations/pl/LC_MESSAGES/django.po b/netbox/translations/pl/LC_MESSAGES/django.po index d7524b25c..fdbd5293c 100644 --- a/netbox/translations/pl/LC_MESSAGES/django.po +++ b/netbox/translations/pl/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Polish (https://app.transifex.com/netbox-community/teams/178115/pl/)\n" @@ -67,26 +67,26 @@ msgstr "Ostatnio używane" msgid "Allowed IPs" msgstr "Dozwolone adresy IP" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Zaloguj się jako {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Wylogowałeś się." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Twoje preferencje zostały zaktualizowane." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Poświadczenia użytkownika uwierzytelnionego LDAP nie mogą być zmieniane w " "NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Twoje hasło zostało pomyślnie zmienione." @@ -134,7 +134,7 @@ msgstr "Wycofane ze służby" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Pierwszorzędny" @@ -231,7 +231,7 @@ msgstr "Grupa terenów (identyfikator)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -994,7 +994,7 @@ msgstr "Atrybuty" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1093,7 +1093,7 @@ msgstr "Sieć dostawców" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1213,9 +1213,9 @@ msgstr "Rola operacyjna" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1317,7 +1317,7 @@ msgstr "Łączność" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1340,7 +1340,7 @@ msgstr "Region" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1711,7 +1711,7 @@ msgstr "zakończenia obwodu wirtualnego" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1967,9 +1967,9 @@ msgstr "Zakończenia" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2143,7 +2143,7 @@ msgstr "Tygodniowy" msgid "30 days" msgstr "30 dni" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Zaktualizowano" @@ -2174,7 +2174,7 @@ msgstr "Zatrzymane" msgid "Cancelled" msgstr "Anulowane" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2806,49 +2806,49 @@ msgstr "IDENTYFIKATOR" msgid "Interval" msgstr "Przedział" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Wersja" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Ostatnia aktualizacja" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Minimalna wersja NetBox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Maksymalna wersja NetBox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Nie znaleziono danych wtyczki" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Autor" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Zainstalowany" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certyfikowany" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Opublikowano" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Zainstalowana wersja" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Najnowsza wersja" @@ -3085,8 +3085,8 @@ msgstr "Tył do przodu" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3215,7 +3215,7 @@ msgstr "Wirtualny" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3226,7 +3226,7 @@ msgid "Virtual interfaces" msgstr "Interfejsy wirtualne" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3613,7 +3613,7 @@ msgstr "Jest pełna głębokość" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3762,7 +3762,7 @@ msgstr "Przypisany VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3783,7 +3783,7 @@ msgstr "Przypisany VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3821,7 +3821,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Zasady tłumaczenia sieci VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3859,7 +3859,7 @@ msgstr "Interfejs LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "Adres MAC" @@ -3867,14 +3867,14 @@ msgstr "Adres MAC" msgid "Primary MAC address (ID)" msgstr "Podstawowy adres MAC (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Podstawowy adres MAC" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Kontekst urządzenia wirtualnego" @@ -3951,8 +3951,8 @@ msgstr "Tagi" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3999,8 +3999,8 @@ msgstr "Strefa czasowa" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4163,7 +4163,7 @@ msgstr "Przepływ powietrza" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4179,7 +4179,7 @@ msgstr "Szafa" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Sprzęt" @@ -4203,15 +4203,24 @@ msgid "Exclude from utilization" msgstr "Wyklucz z wykorzystania" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Typ urządzenia" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4397,8 +4406,8 @@ msgid "Allocated power draw (watts)" msgstr "Przydzielony pobór mocy (waty)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Port zasilania" @@ -4432,7 +4441,7 @@ msgid "Wireless role" msgstr "Rola sieci bezprzewodowej" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4451,7 +4460,7 @@ msgstr "Moduł" msgid "LAG" msgstr "OPÓŹNIENIE" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Konteksty urządzeń wirtualnych" @@ -4479,21 +4488,21 @@ msgstr "Prędkość" msgid "Mode" msgstr "Tryb" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Grupa VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "Nieoznaczone sieci VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4508,16 +4517,16 @@ msgstr "Dodaj oznaczone sieci VLAN" msgid "Remove tagged VLANs" msgstr "Usuń oznaczone sieci VLAN" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Usługa Q-in-Q Usługa VLAN" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Grupa sieci bezprzewodowej sieci LAN" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4525,29 +4534,29 @@ msgid "Wireless LANs" msgstr "Bezprzewodowe sieci LAN" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Adresowanie" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operacja" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4555,7 +4564,7 @@ msgid "Related Interfaces" msgstr "Powiązane interfejsy" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4817,13 +4826,13 @@ msgstr "Lokalny port zasilania zasilający to gniazdko" msgid "Electrical phase (for three-phase circuits)" msgstr "Faza elektryczna (dla obwodów trójfazowych)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Interfejs nadrzędny" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4889,8 +4898,8 @@ msgstr "Rola bezprzewodowa (AP/stacja)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} nie jest przypisany do urządzenia {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Tylny port" @@ -5071,7 +5080,7 @@ msgstr "Rodzaj zasilania (AC/DC)" msgid "Single or three-phase" msgstr "Pojedynczy lub trójfazowy" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5082,7 +5091,7 @@ msgstr "Podstawowy IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Adres IPv4 z maską, np. 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5249,7 +5258,7 @@ msgstr "Uprzejmy" msgid "Mgmt only" msgstr "Tylko MGMT" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5395,7 +5404,7 @@ msgstr "Automatyczne wypełnianie komponentów powiązanych z tym typem modułu" msgid "Characteristics" msgstr "Charakterystyka" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5410,35 +5419,35 @@ msgstr "" "zostanie automatycznie zastąpiony wartością pozycji podczas tworzenia nowego" " modułu." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Szablon portu konsoli" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Szablon portu serwera konsoli" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Szablon portu przedniego" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Szablon interfejsu" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Szablon gniazdka elektrycznego" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Szablon portu zasilania" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Szablon tylnego portu" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5446,14 +5455,14 @@ msgstr "Szablon tylnego portu" msgid "Console Port" msgstr "Port konsoli" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Port serwera konsoli" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5464,7 +5473,7 @@ msgstr "Port serwera konsoli" msgid "Front Port" msgstr "Port przedni" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5477,40 +5486,40 @@ msgstr "Port przedni" msgid "Rear Port" msgstr "Tylny port" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Port zasilania" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Gniazdo zasilania" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Przypisywanie komponentów" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "InventoryItem można przypisać tylko do pojedynczego komponentu." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Interfejs LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtruj sieci VLAN dostępne do przypisania według grup." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Urządzenie dziecięce" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5518,37 +5527,37 @@ msgstr "" "Urządzenia podrzędne muszą być najpierw utworzone i przypisane do terenu " "i szafy urządzenia nadrzędnego." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Port konsoli" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Port serwera konsoli" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Port przedni" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Gniazdo zasilania" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Przedmiot zapasów" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Rola pozycji zapasów" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Interfejs VM" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5566,13 +5575,13 @@ msgstr "Interfejs VM" msgid "Virtual Machine" msgstr "Maszyna wirtualna" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Adres MAC można przypisać tylko do jednego obiektu." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5590,18 +5599,18 @@ msgstr "" "oczekiwane." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Tylne porty" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Wybierz jedno przypisanie portu tylnego dla każdego tworzonego portu " "przedniego." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5610,7 +5619,7 @@ msgstr "" "Liczba szablonów portów przednich do utworzenia ({frontport_count}) musi " "odpowiadać wybranej liczbie pozycji tylnych portów ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5619,18 +5628,18 @@ msgstr "" "Liczba portów przednich do utworzenia ({frontport_count}) musi odpowiadać " "wybranej liczbie pozycji tylnych portów ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Członkowie" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Pozycja początkowa" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5638,7 +5647,7 @@ msgstr "" "Położenie pierwszego urządzenia członkowskiego. Zwiększa się o jeden dla " "każdego dodatkowego członka." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Pozycja musi być określona dla pierwszego członka VC." @@ -6131,6 +6140,7 @@ msgstr "oznaczone sieci VLAN" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q SVLAN" @@ -7358,8 +7368,8 @@ msgid "Power outlets" msgstr "Gniazdka elektryczne" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7397,8 +7407,8 @@ msgid "Module Bay" msgstr "Moduł Bay" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7434,7 +7444,7 @@ msgstr "Przydzielone losowanie (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "Adresy IP" @@ -7445,7 +7455,7 @@ msgid "FHRP Groups" msgstr "Grupy FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7532,8 +7542,8 @@ msgstr "Wysokość U" msgid "Instances" msgstr "Instancje" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7543,8 +7553,8 @@ msgstr "Instancje" msgid "Console Ports" msgstr "Porty konsoli" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7554,8 +7564,8 @@ msgstr "Porty konsoli" msgid "Console Server Ports" msgstr "Porty serwera konsoli" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7565,8 +7575,8 @@ msgstr "Porty serwera konsoli" msgid "Power Ports" msgstr "Porty zasilania" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7576,8 +7586,8 @@ msgstr "Porty zasilania" msgid "Power Outlets" msgstr "Gniazdka elektryczne" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7586,8 +7596,8 @@ msgstr "Gniazdka elektryczne" msgid "Front Ports" msgstr "Porty przednie" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7597,16 +7607,16 @@ msgstr "Porty przednie" msgid "Rear Ports" msgstr "Tylne porty" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Wnęsy na urządzenia" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7675,62 +7685,62 @@ msgstr "Grupy VLAN" msgid "Test case must set peer_termination_type" msgstr "Przypadek testowy musi ustawić peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Odłączony {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Rezerwacje" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Urządzenia poza szafami" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Kontekst konfiguracji" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Konfiguracja renderowania" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Maszyny wirtualne" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Zainstalowane urządzenie {device} w zatoce {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Usunięte urządzenie {device} z zatoki {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Dzieci" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Dodano członka {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "Nie można usunąć urządzenia głównego {device} z wirtualnego podwozia." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Usunięto {device} z wirtualnego podwozia {chassis}" @@ -11676,7 +11686,7 @@ msgstr "Role pozycji zapasów" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "Adresy MAC" @@ -12199,7 +12209,7 @@ msgstr "Wartość" msgid "Dummy Plugin" msgstr "Wtyczka Dummy" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12207,24 +12217,24 @@ msgid "" msgstr "" "Wystąpił błąd renderowania wybranego szablonu eksportu ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Wiersz {i}: Obiekt z identyfikatorem {id} nie istnieje" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Nie {object_type} zostały wybrane." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Zmiana nazwy {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Usunięte {count} {object_type}" @@ -12251,7 +12261,7 @@ msgstr "Zsynchronizowane dane dla {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Zsynchronizowane {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} musi zaimplementować get_children ()" @@ -12526,32 +12536,36 @@ msgstr "Motyw NetBox" msgid "NetBox Logo" msgstr "Logo NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Dokumenty" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Dokumentacja REST API" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "Interfejs API GraphQL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Wsparcie NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Kod źródłowy" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Społeczność" @@ -13562,7 +13576,7 @@ msgid "PoE Type" msgstr "Typ PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Tłumaczenie VLAN" @@ -13615,12 +13629,12 @@ msgstr "Brak interfejsów członka" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Dodaj adres IP" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Dodaj adres MAC" @@ -16016,7 +16030,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Nieznany app_label/model_name dla {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Nieprawidłowy adres IP ustawiony dla {header}: {ip}" diff --git a/netbox/translations/pt/LC_MESSAGES/django.po b/netbox/translations/pt/LC_MESSAGES/django.po index 21e4dcb36..231bb51b7 100644 --- a/netbox/translations/pt/LC_MESSAGES/django.po +++ b/netbox/translations/pt/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Portuguese (https://app.transifex.com/netbox-community/teams/178115/pt/)\n" @@ -67,26 +67,26 @@ msgstr "Usado pela Última Vez" msgid "Allowed IPs" msgstr "IPs Permitidos" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Logado como {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Você se desconectou." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Suas preferências foram atualizadas." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "As credenciais de usuário autenticadas pelo LDAP não podem ser alteradas no " "NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Sua senha foi alterada com sucesso." @@ -134,7 +134,7 @@ msgstr "Descomissionado" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Primário" @@ -231,7 +231,7 @@ msgstr "Grupo de sites (slug)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -994,7 +994,7 @@ msgstr "Atributos" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1093,7 +1093,7 @@ msgstr "Rede do provedor" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1213,9 +1213,9 @@ msgstr "Função operacional" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1317,7 +1317,7 @@ msgstr "Contatos" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1340,7 +1340,7 @@ msgstr "Região" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1712,7 +1712,7 @@ msgstr "terminações de circuito virtual" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1968,9 +1968,9 @@ msgstr "Terminações" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2144,7 +2144,7 @@ msgstr "Semanalmente" msgid "30 days" msgstr "30 dias" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Atualizado" @@ -2175,7 +2175,7 @@ msgstr "Parado" msgid "Cancelled" msgstr "Cancelado" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2812,49 +2812,49 @@ msgstr "ID" msgid "Interval" msgstr "Intervalo" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Versão" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Última Atualização" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Versão Mínima do Netbox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Versão Máxima do Netbox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Nenhum dado do plugin encontrado" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Autor" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Instalado" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Certificado" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Publicado" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Versão Instalada" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Última Versão" @@ -3091,8 +3091,8 @@ msgstr "Trás para frente" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3221,7 +3221,7 @@ msgstr "Virtual" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3232,7 +3232,7 @@ msgid "Virtual interfaces" msgstr "Interfaces virtuais" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3619,7 +3619,7 @@ msgstr "É full-depth" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3768,7 +3768,7 @@ msgstr "VLAN ID Designada " #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3789,7 +3789,7 @@ msgstr "VLAN ID Designada " #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3827,7 +3827,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Política de Tradução de VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3865,7 +3865,7 @@ msgstr "Interface LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "Endereço MAC" @@ -3873,14 +3873,14 @@ msgstr "Endereço MAC" msgid "Primary MAC address (ID)" msgstr "Endereço MAC primário (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Endereço MAC primário" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Contexto de Dispositivo Virtual" @@ -3957,8 +3957,8 @@ msgstr "Etiquetas" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4005,8 +4005,8 @@ msgstr "Fuso horário" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4169,7 +4169,7 @@ msgstr "Fluxo de Ar" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4185,7 +4185,7 @@ msgstr "Rack" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Hardware" @@ -4209,15 +4209,24 @@ msgid "Exclude from utilization" msgstr "Excluir da utilização" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Tipo de Dispositivo" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4403,8 +4412,8 @@ msgid "Allocated power draw (watts)" msgstr "Consumo de energia alocado (Watts)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Porta de alimentação" @@ -4438,7 +4447,7 @@ msgid "Wireless role" msgstr "Função do Wireless" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4457,7 +4466,7 @@ msgstr "Módulo" msgid "LAG" msgstr "LAG" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Contextos de dispositivos virtuais" @@ -4485,21 +4494,21 @@ msgstr "Velocidade" msgid "Mode" msgstr "Modo" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Grupo de VLANs" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN Não Tagueada" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4514,16 +4523,16 @@ msgstr "Adicionar VLANs tagueadas" msgid "Remove tagged VLANs" msgstr "Remover VLANs tagueadas" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "VLAN de Serviço Q-in-Q" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Grupo da Rede Wireless" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4531,29 +4540,29 @@ msgid "Wireless LANs" msgstr "Redes Wireless" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Endereçamento" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operação" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4561,7 +4570,7 @@ msgid "Related Interfaces" msgstr "Interfaces Relacionadas" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4824,13 +4833,13 @@ msgstr "Porta de alimentação local que alimenta esta tomada" msgid "Electrical phase (for three-phase circuits)" msgstr "Fase (para circuitos trifásicos)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Interface pai" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4898,8 +4907,8 @@ msgstr "" "Contexto de dispositivo virtual {vdc} não está associado ao dispositivo " "{device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Porta traseira" @@ -5078,7 +5087,7 @@ msgstr "Tipo de alimentação (AC/DC)" msgid "Single or three-phase" msgstr "Monofásico ou trifásico" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5089,7 +5098,7 @@ msgstr "IPv4 Primário" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Endereço IPv4 com máscara, por exemplo, 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5257,7 +5266,7 @@ msgstr "Tipo" msgid "Mgmt only" msgstr "Somente gerenciamento" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5403,7 +5412,7 @@ msgstr "" msgid "Characteristics" msgstr "Características" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5418,35 +5427,35 @@ msgstr "" " será automaticamente substituído pelo valor da posição ao criar um novo " "módulo." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Modelo da porta de console" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Modelo da porta do servidor de console" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Modelo da porta frontal" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Modelo da interface" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Modelo da tomada elétrica" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Modelo da porta de alimentação" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Modelo da porta traseira" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5454,14 +5463,14 @@ msgstr "Modelo da porta traseira" msgid "Console Port" msgstr "Porta de Console" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Porta do Servidor de Console" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5472,7 +5481,7 @@ msgstr "Porta do Servidor de Console" msgid "Front Port" msgstr "Porta Frontal" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5485,40 +5494,40 @@ msgstr "Porta Frontal" msgid "Rear Port" msgstr "Porta Traseira" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Porta de Alimentação" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Tomada Elétrica" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Atribuição de Componentes" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Um item de inventário só pode ser associado a um único componente." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Interface LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Filtre as VLANs disponíveis para atribuição por grupo." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Dispositivo Filho" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5526,37 +5535,37 @@ msgstr "" "Os dispositivos filhos devem primeiro ser criados e atribuídos ao site e ao " "rack do dispositivo pai." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Porta de console" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Porta do servidor de console" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Porta frontal" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Tomada elétrica" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Item de Inventário" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Função do Item de Inventário" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Interface de VM" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5574,13 +5583,13 @@ msgstr "Interface de VM" msgid "Virtual Machine" msgstr "Máquina Virtual" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "Um endereço MAC só pode ser atribuído a um único objeto." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5598,18 +5607,18 @@ msgstr "" " esperados." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Portas traseiras" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Selecione uma atribuição de porta traseira para cada porta frontal que está " "sendo criada." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5619,7 +5628,7 @@ msgstr "" "deve corresponder ao número selecionado de posições dE porta traseira " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5629,18 +5638,18 @@ msgstr "" "corresponder ao número selecionado de posições de portas traseiras " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Membros" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Posição inicial" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5648,7 +5657,7 @@ msgstr "" "Posição do primeiro dispositivo membro. Aumenta em um para cada membro " "adicional." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "" "Uma posição deve ser especificada para o primeiro membro do chassi virtual." @@ -6140,6 +6149,7 @@ msgstr "VLANs tagueadas" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "SVLAN Q-in-Q" @@ -7376,8 +7386,8 @@ msgid "Power outlets" msgstr "Tomadas elétricas" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7415,8 +7425,8 @@ msgid "Module Bay" msgstr "Compartimento de módulo" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7452,7 +7462,7 @@ msgstr "Consumo alocado (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "Endereços IP" @@ -7463,7 +7473,7 @@ msgid "FHRP Groups" msgstr "Grupos FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7550,8 +7560,8 @@ msgstr "Altura em U" msgid "Instances" msgstr "Instâncias" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7561,8 +7571,8 @@ msgstr "Instâncias" msgid "Console Ports" msgstr "Portas de Console" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7572,8 +7582,8 @@ msgstr "Portas de Console" msgid "Console Server Ports" msgstr "Portas de Servidor de Console" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7583,8 +7593,8 @@ msgstr "Portas de Servidor de Console" msgid "Power Ports" msgstr "Portas de Alimentação" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7594,8 +7604,8 @@ msgstr "Portas de Alimentação" msgid "Power Outlets" msgstr "Tomadas Elétricas" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7604,8 +7614,8 @@ msgstr "Tomadas Elétricas" msgid "Front Ports" msgstr "Portas Frontais" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7615,16 +7625,16 @@ msgstr "Portas Frontais" msgid "Rear Ports" msgstr "Portas Traseiras" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Compartimentos de Dispositivos" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7693,63 +7703,63 @@ msgstr "Grupos de VLANs" msgid "Test case must set peer_termination_type" msgstr "O caso de teste deve definir peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Desconectado {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Reservas" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Dispositivos Não Montados em Rack" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Contexto de Configuração" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Renderização de Configuração" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Máquinas Virtuais" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Dispositivo instalado {device} no compartimento {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Dispositivo {device} removido do compartimento {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Filhos" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Membro {device} adicionado" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "" "Não é possível remover o dispositivo principal {device} do chassi virtual." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Removido {device} do chassi virtual {chassis}" @@ -11703,7 +11713,7 @@ msgstr "Funções dos Itens de Inventário" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "Endereços MAC" @@ -12223,7 +12233,7 @@ msgstr "Valor" msgid "Dummy Plugin" msgstr "Plugin Dummy" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12231,24 +12241,24 @@ msgid "" msgstr "" "Houve um erro ao renderizar o modelo de exportação ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Linha {i}: Objeto com ID {id} não existe" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Nenhum {object_type} foi/foram selecionado(s)." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Renomeado(s) {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Excluído(s) {count} {object_type}" @@ -12276,7 +12286,7 @@ msgstr "Dados sincronizados para {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Sincronizado(s) {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} deve implementar get_children ()" @@ -12552,32 +12562,36 @@ msgstr "Tema do NetBox" msgid "NetBox Logo" msgstr "Logotipo do NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Documentos" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "API REST" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Documentação da API REST" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "API do GraphQL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Suporte NetBox Labs " -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Código-Fonte" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Comunidade" @@ -13589,7 +13603,7 @@ msgid "PoE Type" msgstr "Tipo de PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Tradução de VLAN" @@ -13642,12 +13656,12 @@ msgstr "Nenhuma interface membro" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Adicionar Endereço IP" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Adicionar Endereço MAC" @@ -16042,7 +16056,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "app_label/model_name desconhecido para {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Endereço IP inválido definido para {header}: {ip}" diff --git a/netbox/translations/ru/LC_MESSAGES/django.po b/netbox/translations/ru/LC_MESSAGES/django.po index 1f591008a..d06e4363c 100644 --- a/netbox/translations/ru/LC_MESSAGES/django.po +++ b/netbox/translations/ru/LC_MESSAGES/django.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Russian (https://app.transifex.com/netbox-community/teams/178115/ru/)\n" @@ -73,26 +73,26 @@ msgstr "Последний раз использованный" msgid "Allowed IPs" msgstr "Разрешенные IP-адреса" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Вошел в систему как {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Вы вышли из системы." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Ваши предпочтения обновлены." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Учетные данные пользователя, аутентифицированные по протоколу LDAP, нельзя " "изменить в NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Ваш пароль успешно изменен." @@ -140,7 +140,7 @@ msgstr "Списан" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Начальное" @@ -237,7 +237,7 @@ msgstr "Группа сайтов (слизень)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -1000,7 +1000,7 @@ msgstr "Атрибуты" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1099,7 +1099,7 @@ msgstr "Сеть провайдера" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1219,9 +1219,9 @@ msgstr "Операционная роль" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1323,7 +1323,7 @@ msgstr "Контакты" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1346,7 +1346,7 @@ msgstr "Регион" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1717,7 +1717,7 @@ msgstr "прерывания виртуальных каналов" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1973,9 +1973,9 @@ msgstr "Соединения" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2150,7 +2150,7 @@ msgstr "Еженедельно" msgid "30 days" msgstr "30 дней" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Обновлено" @@ -2181,7 +2181,7 @@ msgstr "Остановлен" msgid "Cancelled" msgstr "Отменено" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2815,49 +2815,49 @@ msgstr "ID" msgid "Interval" msgstr "Интервал" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Версия" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Последнее обновление" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Минимальная версия NetBox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Максимальная версия NetBox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Данные плагина не найдены" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Автор" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Установлен" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Сертифицирован" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Опубликовано" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Установленная версия" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Последняя версия" @@ -3094,8 +3094,8 @@ msgstr "Сзади вперед" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3224,7 +3224,7 @@ msgstr "Виртуальный" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3235,7 +3235,7 @@ msgid "Virtual interfaces" msgstr "Виртуальные интерфейсы" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3622,7 +3622,7 @@ msgstr "Полная глубина" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3771,7 +3771,7 @@ msgstr "Назначенный VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3792,7 +3792,7 @@ msgstr "Назначенный VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3830,7 +3830,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Политика трансляции VLAN (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3868,7 +3868,7 @@ msgstr "Интерфейс LAG (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC-адрес" @@ -3876,14 +3876,14 @@ msgstr "MAC-адрес" msgid "Primary MAC address (ID)" msgstr "Основной MAC-адрес (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Основной MAC-адрес" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Виртуальный контекст" @@ -3960,8 +3960,8 @@ msgstr "Теги" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -4008,8 +4008,8 @@ msgstr "Часовой пояс" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4172,7 +4172,7 @@ msgstr "Воздушный поток" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4188,7 +4188,7 @@ msgstr "Стойка" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Аппаратное обеспечение" @@ -4212,15 +4212,24 @@ msgid "Exclude from utilization" msgstr "Исключить из использования" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Тип устройства" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4406,8 +4415,8 @@ msgid "Allocated power draw (watts)" msgstr "Распределенная потребляемая мощность (Вт)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Порт питания" @@ -4441,7 +4450,7 @@ msgid "Wireless role" msgstr "Роль беспроводной связи" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4460,7 +4469,7 @@ msgstr "Модуль" msgid "LAG" msgstr "LAG" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Виртуальные контексты" @@ -4488,21 +4497,21 @@ msgstr "Скорость" msgid "Mode" msgstr "Режим" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Группа VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN без тегов" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4517,16 +4526,16 @@ msgstr "Добавить тегированные VLAN-ы" msgid "Remove tagged VLANs" msgstr "Удалить тегированные VLAN-ы" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Сервисная VLAN «Q-in-Q»" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Беспроводная группа LAN" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4534,29 +4543,29 @@ msgid "Wireless LANs" msgstr "Беспроводные LANы" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Адресация" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Операция" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4564,7 +4573,7 @@ msgid "Related Interfaces" msgstr "Связанные интерфейсы" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4826,13 +4835,13 @@ msgstr "Локальный порт питания, питающий эту ро msgid "Electrical phase (for three-phase circuits)" msgstr "Электрическая фаза (для трехфазных цепей)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Родительский интерфейс" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4896,8 +4905,8 @@ msgstr "Роль беспроводной сети (точка доступа/с msgid "VDC {vdc} is not assigned to device {device}" msgstr "В ПОСТОЯННОГО ТОКА {vdc} не присвоено устройству {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Задний порт" @@ -5076,7 +5085,7 @@ msgstr "Тип питания (AC/DC)" msgid "Single or three-phase" msgstr "Однофазный или трехфазный" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5087,7 +5096,7 @@ msgstr "Основной IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Адрес IPv4 с маской, напр. 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5256,7 +5265,7 @@ msgstr "Вид" msgid "Mgmt only" msgstr "Только менеджмент" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5402,7 +5411,7 @@ msgstr "Автоматическое заполнение компонентов msgid "Characteristics" msgstr "Характеристики" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5416,35 +5425,35 @@ msgstr "" "[ge, xe]-0/0/[0-9]). Переменная {module}
будет " "автоматически заменена значением позиции при создании нового модуля." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Шаблон консольного порта" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Шаблон порта консольного сервера" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Шаблон переднего порта" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Шаблон интерфейса" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Шаблон розетки питания" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Шаблон порта питания" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Шаблон заднего порта" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5452,14 +5461,14 @@ msgstr "Шаблон заднего порта" msgid "Console Port" msgstr "Консольный порт" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Порт консольного сервера" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5470,7 +5479,7 @@ msgstr "Порт консольного сервера" msgid "Front Port" msgstr "Передний порт" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5483,40 +5492,40 @@ msgstr "Передний порт" msgid "Rear Port" msgstr "Задний порт" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Порт питания" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Розетка питания" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Назначение компонентов" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Инвентарный номер можно присвоить только одному компоненту." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Интерфейс LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Фильтровать доступные к назначению VLAN-ы по группе." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Дочернее устройство" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5524,37 +5533,37 @@ msgstr "" "Сначала необходимо создать дочерние устройства и назначить их сайту и стойке" " родительского устройства." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Консольный порт" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Порт консольного сервера" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Передний порт" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Розетка питания" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Комплектующие" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Роли комплектующих" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Интерфейс виртуальной машины" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5572,13 +5581,13 @@ msgstr "Интерфейс виртуальной машины" msgid "Virtual Machine" msgstr "Виртуальная машина" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "MAC-адрес можно присвоить только одному объекту." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5596,18 +5605,18 @@ msgstr "" " ожидаются." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Задние порты" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Выберите одно назначение заднего порта для каждого создаваемого переднего " "порта." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5617,7 +5626,7 @@ msgstr "" "должно соответствовать выбранному количеству положений задних портов " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5627,18 +5636,18 @@ msgstr "" "соответствовать выбранному количеству положений задних портов " "({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Участники" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Исходное положение" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5646,7 +5655,7 @@ msgstr "" "Положение первого элементного устройства. Увеличивается на единицу за каждый" " дополнительный элемент." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Должность должна быть указана для первого члена VC." @@ -6138,6 +6147,7 @@ msgstr "тегированные VLAN" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Сеть Q-in-Q" @@ -7367,8 +7377,8 @@ msgid "Power outlets" msgstr "Розетки питания" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7406,8 +7416,8 @@ msgid "Module Bay" msgstr "Модульный отсек" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7443,7 +7453,7 @@ msgstr "Выделенная мощность (Вт)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP-адреса" @@ -7454,7 +7464,7 @@ msgid "FHRP Groups" msgstr "Группы FHRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7541,8 +7551,8 @@ msgstr "Высота U" msgid "Instances" msgstr "Инстансы" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7552,8 +7562,8 @@ msgstr "Инстансы" msgid "Console Ports" msgstr "Порты консоли" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7563,8 +7573,8 @@ msgstr "Порты консоли" msgid "Console Server Ports" msgstr "Порты консольного сервера" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7574,8 +7584,8 @@ msgstr "Порты консольного сервера" msgid "Power Ports" msgstr "Порты питания" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7585,8 +7595,8 @@ msgstr "Порты питания" msgid "Power Outlets" msgstr "Розетки питания" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7595,8 +7605,8 @@ msgstr "Розетки питания" msgid "Front Ports" msgstr "Фронтальные порты" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7606,16 +7616,16 @@ msgstr "Фронтальные порты" msgid "Rear Ports" msgstr "Задние порты" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Отсеки для устройств" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7685,62 +7695,62 @@ msgid "Test case must set peer_termination_type" msgstr "" "В тестовом примере должно быть установлено значение peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Отключен {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Резервирование" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Устройства без стоек" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Контекст конфигурации" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Конфигурация рендера" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Виртуальные машины" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Установлено устройство {device} в отсек {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Удалено устройство {device} из отсека {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Потомки" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Добавлен участник {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "Невозможно удалить главное устройство {device} из виртуального шасси." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "{device} удалено из виртуального шасси {chassis}" @@ -11689,7 +11699,7 @@ msgstr "Роли предметов" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC-адреса" @@ -12212,7 +12222,7 @@ msgstr "Значение" msgid "Dummy Plugin" msgstr "Фиктивный плагин" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12220,24 +12230,24 @@ msgid "" msgstr "" "Произошла ошибка при рендеринге выбранного шаблона ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Ряд {i}: Объект с идентификатором {id} не существует" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "{object_type} не были выбраны." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Переименован(-о) {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Удален(-о) {count} {object_type}" @@ -12264,7 +12274,7 @@ msgstr "Синхронизированы данные для {object_type} {obje msgid "Synced {count} {object_type}" msgstr "Синхронизирован(-о) {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} должен реализовать get_children ()" @@ -12539,32 +12549,36 @@ msgstr "Мотив NetBox" msgid "NetBox Logo" msgstr "Логотип NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Документация" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Документация по REST API" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "API GraphQL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Поддержка NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Исходный код" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Сообщество" @@ -13578,7 +13592,7 @@ msgid "PoE Type" msgstr "Тип PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Трансляция VLAN" @@ -13631,12 +13645,12 @@ msgstr "Нет интерфейсов участников" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Добавить IP-адрес" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Добавить MAC-адрес" @@ -16029,7 +16043,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Неизвестный app_label/model_name для {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Неверный IP-адрес установлен для {header}: {ip}" diff --git a/netbox/translations/tr/LC_MESSAGES/django.po b/netbox/translations/tr/LC_MESSAGES/django.po index a3611c1b6..ac9b9ff70 100644 --- a/netbox/translations/tr/LC_MESSAGES/django.po +++ b/netbox/translations/tr/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Turkish (https://app.transifex.com/netbox-community/teams/178115/tr/)\n" @@ -66,26 +66,26 @@ msgstr "Son Kullanım" msgid "Allowed IPs" msgstr "İzin verilen IP'ler" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Olarak oturum açtı {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Oturumu kapattınız." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Tercihleriniz güncellendi." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "LDAP kimliği doğrulanmış kullanıcı kimlik bilgileri NetBox içinde " "değiştirilemez." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Şifreniz başarıyla değiştirildi." @@ -133,7 +133,7 @@ msgstr "Hizmet dışı bırakıldı" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Birincil" @@ -230,7 +230,7 @@ msgstr "Site grubu (kısa ad)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -993,7 +993,7 @@ msgstr "Öznitellikler" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1092,7 +1092,7 @@ msgstr "Sağlayıcı ağı" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1212,9 +1212,9 @@ msgstr "Operasyonel rol" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1316,7 +1316,7 @@ msgstr "İletişim" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1339,7 +1339,7 @@ msgstr "Bölge" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1710,7 +1710,7 @@ msgstr "sanal devre sonlandırmaları" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1966,9 +1966,9 @@ msgstr "Fesih" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2142,7 +2142,7 @@ msgstr "Haftalık" msgid "30 days" msgstr "30 gün" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Güncellendi" @@ -2173,7 +2173,7 @@ msgstr "Durduruldu" msgid "Cancelled" msgstr "İptal Edildi" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2805,49 +2805,49 @@ msgstr "KİMLİK" msgid "Interval" msgstr "Aralık" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Versiyon" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Son Güncelleme" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Minimum NetBox Sürümü" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Maksimum NetBox Sürümü" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Eklenti verisi bulunamadı" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Yazar" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Kurulmuş" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Sertifikalı" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Yayınlandı" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Yüklü Sürüm" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Son Sürüm" @@ -3084,8 +3084,8 @@ msgstr "Arkadan öne" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3214,7 +3214,7 @@ msgstr "Sanal" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3225,7 +3225,7 @@ msgid "Virtual interfaces" msgstr "Sanal arayüzler" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3612,7 +3612,7 @@ msgstr "Tam derinlik mi" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3761,7 +3761,7 @@ msgstr "Atanmış VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3782,7 +3782,7 @@ msgstr "Atanmış VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3820,7 +3820,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "VLAN Çeviri Politikası (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3858,7 +3858,7 @@ msgstr "LAG arabirimi (ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC Adresi" @@ -3866,14 +3866,14 @@ msgstr "MAC Adresi" msgid "Primary MAC address (ID)" msgstr "Birincil MAC adresi (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Birincil MAC adresi" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Sanal Cihaz Bağlamı" @@ -3950,8 +3950,8 @@ msgstr "Etiketler" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3998,8 +3998,8 @@ msgstr "Saat dilimi" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4162,7 +4162,7 @@ msgstr "Hava akışı" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4178,7 +4178,7 @@ msgstr "Raf" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Donanım" @@ -4202,15 +4202,24 @@ msgid "Exclude from utilization" msgstr "Kullanımdan hariç tut" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Cihaz Türü" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4396,8 +4405,8 @@ msgid "Allocated power draw (watts)" msgstr "Tahsis edilen güç çekimi (watt)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Güç bağlantı noktası" @@ -4431,7 +4440,7 @@ msgid "Wireless role" msgstr "Kablosuz rolü" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4450,7 +4459,7 @@ msgstr "Modül" msgid "LAG" msgstr "GECİKME" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Sanal cihaz bağlamları" @@ -4478,21 +4487,21 @@ msgstr "Hız" msgid "Mode" msgstr "Modu" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "VLAN grubu" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "Etiketsiz VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4507,16 +4516,16 @@ msgstr "Etiketli VLAN'lar ekle" msgid "Remove tagged VLANs" msgstr "Etiketli VLAN'ları kaldır" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Q-in-Q Hizmeti VLAN" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Kablosuz LAN grubu" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4524,29 +4533,29 @@ msgid "Wireless LANs" msgstr "Kablosuz LAN'lar" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Adresleme" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Operasyon" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4554,7 +4563,7 @@ msgid "Related Interfaces" msgstr "İlgili Arayüzler" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4814,13 +4823,13 @@ msgstr "Bu prizi besleyen yerel güç portu" msgid "Electrical phase (for three-phase circuits)" msgstr "Elektrik fazı (üç fazlı devreler için)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Ebeveyn arayüzü" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4886,8 +4895,8 @@ msgstr "Kablosuz rolü (AP/istasyon)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} cihaza atanmadı {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Arka bağlantı noktası" @@ -5064,7 +5073,7 @@ msgstr "Besleme tipi (AC/DC)" msgid "Single or three-phase" msgstr "Tek veya üç fazlı" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5075,7 +5084,7 @@ msgstr "Birincil IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "Maskeli IPv4 adresi, örn. 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5242,7 +5251,7 @@ msgstr "Tür" msgid "Mgmt only" msgstr "Sadece Mgmt" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5388,7 +5397,7 @@ msgstr "Bu modül türüyle ilişkili bileşenleri otomatik olarak doldurun" msgid "Characteristics" msgstr "ÖZELLİKLERİ" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5402,35 +5411,35 @@ msgstr "" "[0-9]
). Simge {module}, varsa, yeni bir modül " "oluştururken otomatik olarak konum değeri ile değiştirilecektir." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Konsol bağlantı noktası şablonu" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Konsol sunucusu bağlantı noktası şablonu" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Ön bağlantı noktası şablonu" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Arayüz şablonu" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Elektrik prizi şablonu" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Güç bağlantı noktası şablonu" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Arka bağlantı noktası şablonu" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5438,14 +5447,14 @@ msgstr "Arka bağlantı noktası şablonu" msgid "Console Port" msgstr "Konsol Bağlantı Noktası" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Konsol Sunucusu Bağlantı Noktası" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5456,7 +5465,7 @@ msgstr "Konsol Sunucusu Bağlantı Noktası" msgid "Front Port" msgstr "Ön Bağlantı Noktası" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5469,40 +5478,40 @@ msgstr "Ön Bağlantı Noktası" msgid "Rear Port" msgstr "Arka Bağlantı Noktası" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Güç Bağlantı Noktası" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Güç Çıkışı" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Bileşen Ataması" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Bir InventoryItem yalnızca tek bir bileşene atanabilir." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "LAG arayüzü" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Gruba göre atama için mevcut VLAN'ları filtreleyin." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Çocuk Cihazı" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5510,37 +5519,37 @@ msgstr "" "Alt aygıtlar önce oluşturulmalı ve ana aygıtın sahasına ve rafına " "atanmalıdır." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Konsol bağlantı noktası" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Konsol sunucusu bağlantı noktası" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Ön bağlantı noktası" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Güç çıkışı" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Envanter Öğesi" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Envanter Öğesi Rolü" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "VM Arayüzü" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5558,13 +5567,13 @@ msgstr "VM Arayüzü" msgid "Virtual Machine" msgstr "Sanal Makine" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "MAC adresi yalnızca tek bir nesneye atanabilir." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5582,18 +5591,18 @@ msgstr "" "bekleniyor." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Arka bağlantı noktaları" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Oluşturulan her ön bağlantı noktası için bir arka bağlantı noktası ataması " "seçin." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5602,7 +5611,7 @@ msgstr "" "Oluşturulacak ön bağlantı noktası şablonlarının sayısı ({frontport_count}) " "seçilen arka port konumu sayısıyla eşleşmelidir ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5611,24 +5620,24 @@ msgstr "" "Oluşturulacak ön bağlantı noktalarının sayısı ({frontport_count}) seçilen " "arka port konumu sayısıyla eşleşmelidir ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Üyeler" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Başlangıç pozisyonu" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." msgstr "İlk üye cihazın konumu. Her ek üye için bir artar." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "İlk VC üyesi için bir pozisyon belirtilmelidir." @@ -6105,6 +6114,7 @@ msgstr "etiketli VLAN'lar" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q SVLAN" @@ -7305,8 +7315,8 @@ msgid "Power outlets" msgstr "Elektrik prizleri" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7344,8 +7354,8 @@ msgid "Module Bay" msgstr "Modül Yuvası" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7381,7 +7391,7 @@ msgstr "Tahsis edilen çekiliş (W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP Adresleri" @@ -7392,7 +7402,7 @@ msgid "FHRP Groups" msgstr "FHRP Grupları" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7479,8 +7489,8 @@ msgstr "U Yüksekliği" msgid "Instances" msgstr "Örnekler" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7490,8 +7500,8 @@ msgstr "Örnekler" msgid "Console Ports" msgstr "Konsol Bağlantı Noktaları" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7501,8 +7511,8 @@ msgstr "Konsol Bağlantı Noktaları" msgid "Console Server Ports" msgstr "Konsol Sunucusu Bağlantı Noktaları" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7512,8 +7522,8 @@ msgstr "Konsol Sunucusu Bağlantı Noktaları" msgid "Power Ports" msgstr "Güç Bağlantı Noktaları" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7523,8 +7533,8 @@ msgstr "Güç Bağlantı Noktaları" msgid "Power Outlets" msgstr "Elektrik Prizleri" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7533,8 +7543,8 @@ msgstr "Elektrik Prizleri" msgid "Front Ports" msgstr "Ön Bağlantı Noktaları" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7544,16 +7554,16 @@ msgstr "Ön Bağlantı Noktaları" msgid "Rear Ports" msgstr "Arka Bağlantı Noktaları" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Cihaz Yuvaları" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7622,62 +7632,62 @@ msgstr "VLAN Grupları" msgid "Test case must set peer_termination_type" msgstr "Test senaryosu peer_termination_type ayarlamalıdır" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Bağlantısı kesildi {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Rezervasyon" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Raf Olmayan Cihazlar" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Yapılandırma Bağlamı" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Oluştur Yapılandırması" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Sanal Makineler" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Yüklü cihaz {device} körfezde {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Kaldırılan cihaz {device} körfezden {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Çocuklar" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Eklenen üye {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "Ana aygıt kaldırılamıyor {device} sanal kasadan." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Kaldırıldı {device} sanal kasadan {chassis}" @@ -11596,7 +11606,7 @@ msgstr "Envanter Öğesi Rolleri" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC Adresleri" @@ -12117,7 +12127,7 @@ msgstr "Değer" msgid "Dummy Plugin" msgstr "Sahte Eklenti" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12126,24 +12136,24 @@ msgstr "" "Seçilen dışa aktarma şablonunu oluştururken bir hata oluştu ({template}): " "{error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Satır {i}: Kimliği olan nesne {id} mevcut değil" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Hayır {object_type} seçildi." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Yeniden adlandırıldı {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Silinmiş {count} {object_type}" @@ -12170,7 +12180,7 @@ msgstr "Senkronize edilmiş veriler {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Senkronize {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} get_children () uygulamasını uygulamalıdır" @@ -12445,32 +12455,36 @@ msgstr "NetBox Motif" msgid "NetBox Logo" msgstr "NetBox Logosu" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Dokümanlar" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "REST API belgeleri" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "NetBox Labs Desteği" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Kaynak Kodu" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Topluluk" @@ -13484,7 +13498,7 @@ msgid "PoE Type" msgstr "PoE Tipi" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "VLAN Çeviri" @@ -13537,12 +13551,12 @@ msgstr "Üye arabirimi yok" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "IP Adresi Ekle" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "MAC Adresi Ekle" @@ -15919,7 +15933,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Bilinmeyen app_label/model_name {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Geçersiz IP adresi ayarlandı {header}: {ip}" diff --git a/netbox/translations/uk/LC_MESSAGES/django.po b/netbox/translations/uk/LC_MESSAGES/django.po index a21eb103c..aae2c9730 100644 --- a/netbox/translations/uk/LC_MESSAGES/django.po +++ b/netbox/translations/uk/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Ukrainian (https://app.transifex.com/netbox-community/teams/178115/uk/)\n" @@ -66,25 +66,25 @@ msgstr "Використано востаннє" msgid "Allowed IPs" msgstr "Дозволені IP-адреси" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "Ввійшов в систему як {user}." -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "Ви вийшли з системи." -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "Ваші налаштування було оновлено." -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "" "Облікові дані користувача, підтверджені LDAP, неможливо змінити в NetBox." -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "Ваш пароль успішно змінено." @@ -132,7 +132,7 @@ msgstr "Виведені з експлуатації" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "Первинний" @@ -229,7 +229,7 @@ msgstr "Група тех. майданчиків (скорочення)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -992,7 +992,7 @@ msgstr "Атрибути" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1091,7 +1091,7 @@ msgstr "Мережа провайдера" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1211,9 +1211,9 @@ msgstr "Операційна роль" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1315,7 +1315,7 @@ msgstr "Контакти" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1338,7 +1338,7 @@ msgstr "Регіон" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1710,7 +1710,7 @@ msgstr "завершення віртуальних схем" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1966,9 +1966,9 @@ msgstr "Кінці" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2142,7 +2142,7 @@ msgstr "Щотижневий" msgid "30 days" msgstr "30 днів" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "Оновлено" @@ -2173,7 +2173,7 @@ msgstr "Зупинено" msgid "Cancelled" msgstr "Скасовано" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2804,49 +2804,49 @@ msgstr "Ідентифікатор" msgid "Interval" msgstr "Інтервал" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "Версія" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "Останнє оновлення" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Мінімальна версія NetBox" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Максимальна версія NetBox" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "Не знайдено даних плагіна" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "Автор" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "Встановлено" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "Сертифіковано" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "Опубліковано" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "Встановлена версія" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "Найновіша версія" @@ -3083,8 +3083,8 @@ msgstr "Ззаду спереду" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3213,7 +3213,7 @@ msgstr "Віртуальний" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3224,7 +3224,7 @@ msgid "Virtual interfaces" msgstr "Віртуальні інтерфейси" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3611,7 +3611,7 @@ msgstr "Це повна глибина" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3760,7 +3760,7 @@ msgstr "Призначений VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3781,7 +3781,7 @@ msgstr "Призначений VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3819,7 +3819,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "Політика перекладу VLAN (ідентифікатор)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3857,7 +3857,7 @@ msgstr "Інтерфейс LAG (ідентифікатор)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC-адреса" @@ -3865,14 +3865,14 @@ msgstr "MAC-адреса" msgid "Primary MAC address (ID)" msgstr "Основна MAC-адреса (ідентифікатор)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "Основна MAC-адреса" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "Контекст віртуального пристрою" @@ -3949,8 +3949,8 @@ msgstr "Мітки" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3997,8 +3997,8 @@ msgstr "Часовий пояс" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4161,7 +4161,7 @@ msgstr "Потік повітря" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4177,7 +4177,7 @@ msgstr "Стійка" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "Апаратне забезпечення" @@ -4201,15 +4201,24 @@ msgid "Exclude from utilization" msgstr "Виключити з утилізації" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "Тип пристрою" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4395,8 +4404,8 @@ msgid "Allocated power draw (watts)" msgstr "Виділена споживана потужність (Вт)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "Порт живлення" @@ -4430,7 +4439,7 @@ msgid "Wireless role" msgstr "Бездротова роль" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4449,7 +4458,7 @@ msgstr "Модуль" msgid "LAG" msgstr "LAG" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "Контексти віртуальних пристроїв" @@ -4477,21 +4486,21 @@ msgstr "Швидкість" msgid "Mode" msgstr "Режим" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "Група VLAN" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "VLAN без міток" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4506,16 +4515,16 @@ msgstr "Додати VLAN'и з мітками" msgid "Remove tagged VLANs" msgstr "Видалити мітки з VLAN'ів" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Сервісна локальна мережа Q-in-Q" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "Група бездротової локальної мережі" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4523,29 +4532,29 @@ msgid "Wireless LANs" msgstr "Бездротові локальні мережі" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "Адресація" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "Операція" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4553,7 +4562,7 @@ msgid "Related Interfaces" msgstr "Пов'язані інтерфейси" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4815,13 +4824,13 @@ msgstr "Локальний порт живлення, який живить цю msgid "Electrical phase (for three-phase circuits)" msgstr "Електрична фаза (для трифазних ланцюгів)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "Батьківський інтерфейс" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4889,8 +4898,8 @@ msgstr "" "Джерело живлення постійного струму {vdc} не призначається до пристрою " "{device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "Задній порт" @@ -5069,7 +5078,7 @@ msgstr "Тип живлення (змінній/постійний струм)" msgid "Single or three-phase" msgstr "Однофазний або трифазний (струм)" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5080,7 +5089,7 @@ msgstr "Первинна адреса IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "IPv4 адреса з маскою, наприклад 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5249,7 +5258,7 @@ msgstr "Вид" msgid "Mgmt only" msgstr "Тільки управління" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5395,7 +5404,7 @@ msgstr "Автоматично заповнювати компоненти, по msgid "Characteristics" msgstr "Характеристики" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5409,35 +5418,35 @@ msgstr "" "[ге, хе] -0/0/ [0-9]). Жетон {module}, якщо є, " "буде автоматично замінено значенням позиції при створенні нового модуля." -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "Шаблон порту консолі" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "Шаблон порту консольного сервера" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "Шаблон фронтального порту" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "Шаблон інтерфейсу" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "Шаблон електрічної розетки" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "Шаблон порту живлення" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "Шаблон порту ззаду" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5445,14 +5454,14 @@ msgstr "Шаблон порту ззаду" msgid "Console Port" msgstr "Порт консолі" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Порт консольного сервера" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5463,7 +5472,7 @@ msgstr "Порт консольного сервера" msgid "Front Port" msgstr "Передній порт" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5476,40 +5485,40 @@ msgstr "Передній порт" msgid "Rear Port" msgstr "Порт ззаду" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "Порт живлення" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "Електрична розетка" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "Призначення компонентів" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "Елемент інвентаря можна призначити лише одному компоненту." -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "Інтерфейс LAG" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "Фільтр VLAN'ів, доступних для призначення за групами." -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "Підпорядкований пристрій" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." @@ -5517,37 +5526,37 @@ msgstr "" "Підпорядковані пристрої спочатку повинні бути створені та присвоєні до тех. " "майданчику та стійки батьківського пристрою." -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Консольний порт" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Порт консольного сервера" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "Передній порт" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "Розетка живлення" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "Елемент інвентаря" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "Роль елемента інвентаря" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "Інтерфейс VM" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5565,13 +5574,13 @@ msgstr "Інтерфейс VM" msgid "Virtual Machine" msgstr "Віртуальна машина" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "MAC-адресу можна призначити лише одному об'єкту." #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5589,18 +5598,18 @@ msgstr "" "очікуються." #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "Порти ззаду" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "" "Виберіть одне призначення порту ззаду для кожного створюваного переднього " "порту." -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " @@ -5610,7 +5619,7 @@ msgstr "" "({frontport_count}) повинна відповідати вибраній кількості позицій портів " "ззаду ({rearport_count})." -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " @@ -5619,18 +5628,18 @@ msgstr "" "Кількість передніх портів, які потрібно створити ({frontport_count}) повинна" " відповідати вибраній кількості позицій портів ззаду ({rearport_count})." -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "Члени" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "Початкова позиція" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." @@ -5638,7 +5647,7 @@ msgstr "" "Положення пристрою першого члена. Збільшується на одного для кожного " "додаткового члена." -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "Позиція повинна бути вказана для першого члена VC." @@ -6129,6 +6138,7 @@ msgstr "VLAN'и з мітками" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-в-Q SVLAN" @@ -7364,8 +7374,8 @@ msgid "Power outlets" msgstr "Розетки" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7403,8 +7413,8 @@ msgid "Module Bay" msgstr "Резервуар модулів" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7440,7 +7450,7 @@ msgstr "Виділена потужність (Вт)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP-адреси" @@ -7451,7 +7461,7 @@ msgid "FHRP Groups" msgstr "Групи FHRP/VRRP" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7538,8 +7548,8 @@ msgstr "Висота юніта(U)" msgid "Instances" msgstr "Екземпляри" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7549,8 +7559,8 @@ msgstr "Екземпляри" msgid "Console Ports" msgstr "Консольні порти" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7560,8 +7570,8 @@ msgstr "Консольні порти" msgid "Console Server Ports" msgstr "Порти консольного сервера" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7571,8 +7581,8 @@ msgstr "Порти консольного сервера" msgid "Power Ports" msgstr "Порти живлення" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7582,8 +7592,8 @@ msgstr "Порти живлення" msgid "Power Outlets" msgstr "Розетки" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7592,8 +7602,8 @@ msgstr "Розетки" msgid "Front Ports" msgstr "Передні порти" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7603,16 +7613,16 @@ msgstr "Передні порти" msgid "Rear Ports" msgstr "Задні порти" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "Відсіки для пристроїв" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7681,62 +7691,62 @@ msgstr "Групи VLAN" msgid "Test case must set peer_termination_type" msgstr "Тестовий випадок повинен встановити peer_termination_type" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "Відключено {count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "Бронювання" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "Пристрої без можливості кріплення у стійку" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "Контекст конфігурації" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "Відтворення конфігурації" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "Віртуальні машини" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "Встановлений пристрій {device} в бухті {device_bay}." -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "Видалений пристрій {device} з бухти {device_bay}." -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "Підпорядкований" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "Доданий член {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "Неможливо видалити головний пристрій {device} від віртуального шасі." -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "Вилучено {device} з віртуального шасі {chassis}" @@ -11682,7 +11692,7 @@ msgstr "Ролі елементів інвентаря" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC-адреси" @@ -12201,7 +12211,7 @@ msgstr "Значення" msgid "Dummy Plugin" msgstr "Фіктивний плагін" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " @@ -12210,24 +12220,24 @@ msgstr "" "Виникла помилка при рендерингу вибраного шаблону експорту ({template}): " "{error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "Ряд {i}: Об'єкт з ідентифікатором {id} не існує" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "Ні {object_type} були обрані." -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "Перейменовано {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "Видалено {count} {object_type}" @@ -12254,7 +12264,7 @@ msgstr "Синхронізовані дані для {object_type} {object}." msgid "Synced {count} {object_type}" msgstr "Синхронізовано {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name} повинен реалізувати get_children()" @@ -12531,32 +12541,36 @@ msgstr "NetBox Motif" msgid "NetBox Logo" msgstr "Логотип NetBox" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "Документація" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "Документація REST API" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "Графічний інтерфейс QL" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "Підтримка NetBox Labs" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "Вихідний код" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "Спільнота" @@ -13565,7 +13579,7 @@ msgid "PoE Type" msgstr "Тип PoE" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "Переклад VLAN" @@ -13618,12 +13632,12 @@ msgstr "Немає інтерфейсів учасників" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "Додати IP-адресу" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "Додати MAC-адресу" @@ -16010,7 +16024,7 @@ msgstr "" msgid "Unknown app_label/model_name for {name}" msgstr "Невідома мітка_додатка/назва_моделі для {name}" -#: netbox/utilities/request.py:76 +#: netbox/utilities/request.py:79 #, python-brace-format msgid "Invalid IP address set for {header}: {ip}" msgstr "Невірна IP-адреса, встановлена для {header}: {ip}" diff --git a/netbox/translations/zh/LC_MESSAGES/django.po b/netbox/translations/zh/LC_MESSAGES/django.po index b89700900..b52fff222 100644 --- a/netbox/translations/zh/LC_MESSAGES/django.po +++ b/netbox/translations/zh/LC_MESSAGES/django.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-30 05:01+0000\n" +"POT-Creation-Date: 2025-05-01 05:01+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2025\n" "Language-Team: Chinese (https://app.transifex.com/netbox-community/teams/178115/zh/)\n" @@ -75,24 +75,24 @@ msgstr "最后使用" msgid "Allowed IPs" msgstr "允许的IP" -#: netbox/account/views.py:115 +#: netbox/account/views.py:116 #, python-brace-format msgid "Logged in as {user}." msgstr "以身份登录 {user}。" -#: netbox/account/views.py:171 +#: netbox/account/views.py:172 msgid "You have logged out." msgstr "您已注销。" -#: netbox/account/views.py:223 +#: netbox/account/views.py:224 msgid "Your preferences have been updated." msgstr "你的首选项已更新。" -#: netbox/account/views.py:251 +#: netbox/account/views.py:252 msgid "LDAP-authenticated user credentials cannot be changed within NetBox." msgstr "无法在 Netbox 中更改经过 LDAP 身份验证的用户凭据。" -#: netbox/account/views.py:266 +#: netbox/account/views.py:267 msgid "Your password has been changed successfully." msgstr "您的密码已成功更改。" @@ -140,7 +140,7 @@ msgstr "退役" #: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611 #: netbox/templates/dcim/interface.html:135 -#: netbox/templates/virtualization/vminterface.html:77 +#: netbox/templates/virtualization/vminterface.html:83 #: netbox/tenancy/choices.py:17 msgid "Primary" msgstr "主要联系人" @@ -237,7 +237,7 @@ msgstr "站点组(缩写)" #: netbox/dcim/forms/filtersets.py:1705 netbox/dcim/forms/filtersets.py:1729 #: netbox/dcim/forms/model_forms.py:141 netbox/dcim/forms/model_forms.py:169 #: netbox/dcim/forms/model_forms.py:243 netbox/dcim/forms/model_forms.py:473 -#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:389 +#: netbox/dcim/forms/model_forms.py:734 netbox/dcim/forms/object_create.py:395 #: netbox/dcim/tables/devices.py:163 netbox/dcim/tables/power.py:26 #: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:121 #: netbox/dcim/tables/racks.py:206 netbox/dcim/tables/sites.py:133 @@ -1000,7 +1000,7 @@ msgstr "属性" #: netbox/circuits/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:143 netbox/dcim/forms/model_forms.py:185 #: netbox/dcim/forms/model_forms.py:274 netbox/dcim/forms/model_forms.py:331 -#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1744 +#: netbox/dcim/forms/model_forms.py:780 netbox/dcim/forms/model_forms.py:1775 #: netbox/ipam/forms/model_forms.py:67 netbox/ipam/forms/model_forms.py:84 #: netbox/ipam/forms/model_forms.py:119 netbox/ipam/forms/model_forms.py:141 #: netbox/ipam/forms/model_forms.py:166 netbox/ipam/forms/model_forms.py:233 @@ -1099,7 +1099,7 @@ msgstr "运营商网络" #: netbox/dcim/forms/bulk_import.py:255 netbox/dcim/forms/bulk_import.py:1106 #: netbox/dcim/forms/filtersets.py:368 netbox/dcim/forms/filtersets.py:778 #: netbox/dcim/forms/filtersets.py:1598 netbox/dcim/forms/model_forms.py:256 -#: netbox/dcim/forms/model_forms.py:1090 netbox/dcim/forms/model_forms.py:1559 +#: netbox/dcim/forms/model_forms.py:1121 netbox/dcim/forms/model_forms.py:1590 #: netbox/dcim/forms/object_import.py:182 netbox/dcim/tables/devices.py:179 #: netbox/dcim/tables/devices.py:840 netbox/dcim/tables/devices.py:966 #: netbox/dcim/tables/devicetypes.py:311 netbox/dcim/tables/racks.py:128 @@ -1219,9 +1219,9 @@ msgstr "操作角色" #: netbox/circuits/forms/bulk_import.py:259 #: netbox/circuits/forms/model_forms.py:368 #: netbox/circuits/tables/virtual_circuits.py:112 -#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1164 -#: netbox/dcim/forms/model_forms.py:1433 netbox/dcim/forms/model_forms.py:1600 -#: netbox/dcim/forms/model_forms.py:1635 netbox/dcim/forms/model_forms.py:1765 +#: netbox/dcim/forms/bulk_import.py:1237 netbox/dcim/forms/model_forms.py:1195 +#: netbox/dcim/forms/model_forms.py:1464 netbox/dcim/forms/model_forms.py:1631 +#: netbox/dcim/forms/model_forms.py:1666 netbox/dcim/forms/model_forms.py:1796 #: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1140 #: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:290 #: netbox/ipam/forms/model_forms.py:299 netbox/ipam/tables/fhrp.py:64 @@ -1323,7 +1323,7 @@ msgstr "联系" #: netbox/dcim/forms/filtersets.py:1146 netbox/dcim/forms/filtersets.py:1185 #: netbox/dcim/forms/filtersets.py:1673 netbox/dcim/forms/filtersets.py:1697 #: netbox/dcim/forms/filtersets.py:1721 netbox/dcim/forms/model_forms.py:114 -#: netbox/dcim/forms/object_create.py:373 netbox/dcim/tables/devices.py:153 +#: netbox/dcim/forms/object_create.py:379 netbox/dcim/tables/devices.py:153 #: netbox/dcim/tables/sites.py:85 netbox/extras/filtersets.py:503 #: netbox/ipam/forms/bulk_edit.py:458 netbox/ipam/forms/filtersets.py:226 #: netbox/ipam/forms/filtersets.py:439 netbox/ipam/forms/filtersets.py:530 @@ -1346,7 +1346,7 @@ msgstr "地区" #: netbox/dcim/forms/filtersets.py:348 netbox/dcim/forms/filtersets.py:431 #: netbox/dcim/forms/filtersets.py:745 netbox/dcim/forms/filtersets.py:964 #: netbox/dcim/forms/filtersets.py:1037 netbox/dcim/forms/filtersets.py:1151 -#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:381 +#: netbox/dcim/forms/filtersets.py:1190 netbox/dcim/forms/object_create.py:387 #: netbox/extras/filtersets.py:520 netbox/ipam/forms/bulk_edit.py:463 #: netbox/ipam/forms/filtersets.py:156 netbox/ipam/forms/filtersets.py:231 #: netbox/ipam/forms/filtersets.py:444 netbox/ipam/forms/filtersets.py:535 @@ -1717,7 +1717,7 @@ msgstr "虚拟电路终止" #: netbox/circuits/tables/providers.py:67 #: netbox/circuits/tables/providers.py:97 #: netbox/circuits/tables/virtual_circuits.py:18 netbox/core/tables/data.py:16 -#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:44 +#: netbox/core/tables/jobs.py:14 netbox/core/tables/plugins.py:50 #: netbox/core/tables/tasks.py:11 netbox/core/tables/tasks.py:115 #: netbox/dcim/forms/filtersets.py:64 netbox/dcim/forms/object_create.py:43 #: netbox/dcim/tables/devices.py:63 netbox/dcim/tables/devices.py:103 @@ -1973,9 +1973,9 @@ msgstr "终端" #: netbox/dcim/forms/filtersets.py:1575 netbox/dcim/forms/filtersets.py:1592 #: netbox/dcim/forms/filtersets.py:1689 netbox/dcim/forms/filtersets.py:1713 #: netbox/dcim/forms/filtersets.py:1737 netbox/dcim/forms/model_forms.py:644 -#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1231 -#: netbox/dcim/forms/model_forms.py:1716 netbox/dcim/forms/model_forms.py:1787 -#: netbox/dcim/forms/object_create.py:254 netbox/dcim/tables/connections.py:22 +#: netbox/dcim/forms/model_forms.py:861 netbox/dcim/forms/model_forms.py:1262 +#: netbox/dcim/forms/model_forms.py:1747 netbox/dcim/forms/model_forms.py:1818 +#: netbox/dcim/forms/object_create.py:260 netbox/dcim/tables/connections.py:22 #: netbox/dcim/tables/connections.py:41 netbox/dcim/tables/connections.py:60 #: netbox/dcim/tables/devices.py:295 netbox/dcim/tables/devices.py:380 #: netbox/dcim/tables/devices.py:421 netbox/dcim/tables/devices.py:463 @@ -2149,7 +2149,7 @@ msgstr "周" msgid "30 days" msgstr "30天" -#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:63 +#: netbox/core/choices.py:103 netbox/core/tables/plugins.py:69 #: netbox/templates/generic/object.html:61 msgid "Updated" msgstr "更新于" @@ -2180,7 +2180,7 @@ msgstr "已停止" msgid "Cancelled" msgstr "已取消" -#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51 +#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:57 #: netbox/templates/core/plugin.html:88 #: netbox/templates/dcim/interface.html:273 msgid "Local" @@ -2805,49 +2805,49 @@ msgstr "ID" msgid "Interval" msgstr "间隔" -#: netbox/core/tables/plugins.py:14 netbox/templates/vpn/ipsecprofile.html:44 +#: netbox/core/tables/plugins.py:20 netbox/templates/vpn/ipsecprofile.html:44 #: netbox/vpn/forms/bulk_edit.py:141 netbox/vpn/forms/bulk_import.py:172 #: netbox/vpn/tables/crypto.py:61 msgid "Version" msgstr "版本" -#: netbox/core/tables/plugins.py:19 netbox/templates/core/datafile.html:38 +#: netbox/core/tables/plugins.py:25 netbox/templates/core/datafile.html:38 msgid "Last Updated" msgstr "最后更新" -#: netbox/core/tables/plugins.py:23 +#: netbox/core/tables/plugins.py:29 msgid "Minimum NetBox Version" msgstr "Netbox 最低版本" -#: netbox/core/tables/plugins.py:27 +#: netbox/core/tables/plugins.py:33 msgid "Maximum NetBox Version" msgstr "Netbox 最高版本" -#: netbox/core/tables/plugins.py:31 netbox/core/tables/plugins.py:74 +#: netbox/core/tables/plugins.py:37 netbox/core/tables/plugins.py:80 msgid "No plugin data found" msgstr "未找到插件数据" -#: netbox/core/tables/plugins.py:48 netbox/templates/core/plugin.html:62 +#: netbox/core/tables/plugins.py:54 netbox/templates/core/plugin.html:62 msgid "Author" msgstr "作者" -#: netbox/core/tables/plugins.py:54 +#: netbox/core/tables/plugins.py:60 msgid "Installed" msgstr "已安装" -#: netbox/core/tables/plugins.py:57 netbox/templates/core/plugin.html:84 +#: netbox/core/tables/plugins.py:63 netbox/templates/core/plugin.html:84 msgid "Certified" msgstr "已认证" -#: netbox/core/tables/plugins.py:60 +#: netbox/core/tables/plugins.py:66 msgid "Published" msgstr "已出版" -#: netbox/core/tables/plugins.py:66 +#: netbox/core/tables/plugins.py:72 msgid "Installed Version" msgstr "已安装的版本" -#: netbox/core/tables/plugins.py:70 +#: netbox/core/tables/plugins.py:76 msgid "Latest Version" msgstr "最新版本" @@ -3084,8 +3084,8 @@ msgstr "从后向前" #: netbox/dcim/forms/bulk_import.py:593 netbox/dcim/forms/bulk_import.py:863 #: netbox/dcim/forms/bulk_import.py:1118 netbox/dcim/forms/filtersets.py:235 #: netbox/dcim/forms/model_forms.py:76 netbox/dcim/forms/model_forms.py:95 -#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1082 -#: netbox/dcim/forms/model_forms.py:1551 +#: netbox/dcim/forms/model_forms.py:174 netbox/dcim/forms/model_forms.py:1113 +#: netbox/dcim/forms/model_forms.py:1582 #: netbox/dcim/forms/object_import.py:177 netbox/dcim/tables/devices.py:689 #: netbox/dcim/tables/devices.py:899 netbox/dcim/tables/devices.py:986 #: netbox/dcim/tables/devices.py:1146 netbox/extras/tables/tables.py:226 @@ -3214,7 +3214,7 @@ msgstr "虚拟" #: netbox/dcim/choices.py:856 netbox/dcim/choices.py:1100 #: netbox/dcim/forms/bulk_edit.py:1578 netbox/dcim/forms/filtersets.py:1384 -#: netbox/dcim/forms/model_forms.py:1007 netbox/dcim/forms/model_forms.py:1445 +#: netbox/dcim/forms/model_forms.py:1023 netbox/dcim/forms/model_forms.py:1476 #: netbox/netbox/navigation/menu.py:146 netbox/netbox/navigation/menu.py:150 #: netbox/templates/dcim/interface.html:267 msgid "Wireless" @@ -3225,7 +3225,7 @@ msgid "Virtual interfaces" msgstr "虚拟接口" #: netbox/dcim/choices.py:1026 netbox/dcim/forms/bulk_edit.py:1431 -#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:993 +#: netbox/dcim/forms/bulk_import.py:870 netbox/dcim/forms/model_forms.py:1005 #: netbox/dcim/tables/devices.py:693 netbox/templates/dcim/interface.html:112 #: netbox/templates/virtualization/vminterface.html:43 #: netbox/virtualization/forms/bulk_edit.py:194 @@ -3612,7 +3612,7 @@ msgstr "是否全尺寸" #: netbox/dcim/filtersets.py:1111 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/forms/filtersets.py:1439 netbox/dcim/forms/filtersets.py:1645 -#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1762 +#: netbox/dcim/forms/filtersets.py:1650 netbox/dcim/forms/model_forms.py:1793 #: netbox/dcim/models/devices.py:1505 netbox/dcim/models/devices.py:1526 #: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:268 @@ -3761,7 +3761,7 @@ msgstr "指定VID" #: netbox/dcim/filtersets.py:1772 netbox/dcim/forms/bulk_edit.py:1544 #: netbox/dcim/forms/bulk_import.py:921 netbox/dcim/forms/filtersets.py:1492 -#: netbox/dcim/forms/model_forms.py:1411 +#: netbox/dcim/forms/model_forms.py:1442 #: netbox/dcim/models/device_components.py:752 #: netbox/dcim/tables/devices.py:647 netbox/ipam/filtersets.py:335 #: netbox/ipam/filtersets.py:346 netbox/ipam/filtersets.py:478 @@ -3782,7 +3782,7 @@ msgstr "指定VID" #: netbox/templates/ipam/ipaddress.html:18 #: netbox/templates/ipam/iprange.html:40 netbox/templates/ipam/prefix.html:19 #: netbox/templates/ipam/vrf.html:7 netbox/templates/ipam/vrf.html:13 -#: netbox/templates/virtualization/vminterface.html:84 +#: netbox/templates/virtualization/vminterface.html:90 #: netbox/virtualization/forms/bulk_edit.py:243 #: netbox/virtualization/forms/bulk_import.py:177 #: netbox/virtualization/forms/filtersets.py:236 @@ -3820,7 +3820,7 @@ msgid "VLAN Translation Policy (ID)" msgstr "VLAN 转换策略 (ID)" #: netbox/dcim/filtersets.py:1800 netbox/dcim/forms/filtersets.py:1463 -#: netbox/dcim/forms/model_forms.py:1428 +#: netbox/dcim/forms/model_forms.py:1459 #: netbox/dcim/models/device_components.py:571 #: netbox/ipam/forms/filtersets.py:503 netbox/ipam/forms/model_forms.py:711 #: netbox/templates/ipam/vlantranslationpolicy.html:11 @@ -3858,7 +3858,7 @@ msgstr "链路聚合接口(ID)" #: netbox/dcim/tables/devices.py:1135 netbox/templates/dcim/interface.html:131 #: netbox/templates/dcim/macaddress.html:11 #: netbox/templates/dcim/macaddress.html:14 -#: netbox/templates/virtualization/vminterface.html:73 +#: netbox/templates/virtualization/vminterface.html:79 msgid "MAC Address" msgstr "MAC 地址" @@ -3866,14 +3866,14 @@ msgstr "MAC 地址" msgid "Primary MAC address (ID)" msgstr "主 MAC 地址 (ID)" -#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1415 +#: netbox/dcim/filtersets.py:1877 netbox/dcim/forms/model_forms.py:1446 #: netbox/virtualization/filtersets.py:279 #: netbox/virtualization/forms/model_forms.py:311 msgid "Primary MAC address" msgstr "主 MAC 地址" #: netbox/dcim/filtersets.py:1899 netbox/dcim/filtersets.py:1911 -#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1742 +#: netbox/dcim/forms/filtersets.py:1399 netbox/dcim/forms/model_forms.py:1773 #: netbox/templates/dcim/virtualdevicecontext.html:15 msgid "Virtual Device Context" msgstr "虚拟设备上下文" @@ -3950,8 +3950,8 @@ msgstr "标签" #: netbox/dcim/forms/bulk_create.py:112 netbox/dcim/forms/filtersets.py:1562 #: netbox/dcim/forms/model_forms.py:498 netbox/dcim/forms/model_forms.py:557 -#: netbox/dcim/forms/object_create.py:202 -#: netbox/dcim/forms/object_create.py:351 netbox/dcim/tables/devices.py:175 +#: netbox/dcim/forms/object_create.py:208 +#: netbox/dcim/forms/object_create.py:357 netbox/dcim/tables/devices.py:175 #: netbox/dcim/tables/devices.py:740 netbox/dcim/tables/devicetypes.py:253 #: netbox/templates/dcim/device.html:43 netbox/templates/dcim/device.html:131 #: netbox/templates/dcim/modulebay.html:38 @@ -3996,8 +3996,8 @@ msgstr "时区" #: netbox/dcim/forms/filtersets.py:996 netbox/dcim/forms/filtersets.py:1603 #: netbox/dcim/forms/model_forms.py:211 netbox/dcim/forms/model_forms.py:345 #: netbox/dcim/forms/model_forms.py:357 netbox/dcim/forms/model_forms.py:404 -#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1095 -#: netbox/dcim/forms/model_forms.py:1564 +#: netbox/dcim/forms/model_forms.py:445 netbox/dcim/forms/model_forms.py:1126 +#: netbox/dcim/forms/model_forms.py:1595 #: netbox/dcim/forms/object_import.py:188 netbox/dcim/tables/devices.py:107 #: netbox/dcim/tables/devices.py:182 netbox/dcim/tables/devices.py:969 #: netbox/dcim/tables/devicetypes.py:85 netbox/dcim/tables/devicetypes.py:315 @@ -4160,7 +4160,7 @@ msgstr "气流方向" #: netbox/dcim/forms/filtersets.py:1084 netbox/dcim/forms/filtersets.py:1216 #: netbox/dcim/forms/model_forms.py:271 netbox/dcim/forms/model_forms.py:314 #: netbox/dcim/forms/model_forms.py:489 netbox/dcim/forms/model_forms.py:767 -#: netbox/dcim/forms/object_create.py:398 netbox/dcim/tables/devices.py:171 +#: netbox/dcim/forms/object_create.py:404 netbox/dcim/tables/devices.py:171 #: netbox/dcim/tables/power.py:70 netbox/dcim/tables/racks.py:216 #: netbox/ipam/forms/filtersets.py:459 netbox/templates/dcim/device.html:30 #: netbox/templates/dcim/inc/cable_termination.html:16 @@ -4176,7 +4176,7 @@ msgstr "机柜" #: netbox/dcim/forms/filtersets.py:326 netbox/dcim/forms/filtersets.py:399 #: netbox/dcim/forms/filtersets.py:482 netbox/dcim/forms/filtersets.py:609 #: netbox/dcim/forms/filtersets.py:722 netbox/dcim/forms/filtersets.py:944 -#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1632 +#: netbox/dcim/forms/model_forms.py:681 netbox/dcim/forms/model_forms.py:1663 #: netbox/templates/dcim/device_edit.html:22 msgid "Hardware" msgstr "硬件" @@ -4200,15 +4200,24 @@ msgid "Exclude from utilization" msgstr "从利用率中排除" #: netbox/dcim/forms/bulk_edit.py:559 netbox/dcim/forms/model_forms.py:377 -#: netbox/dcim/tables/devicetypes.py:82 netbox/templates/dcim/device.html:88 +#: netbox/dcim/forms/model_forms.py:920 netbox/dcim/forms/model_forms.py:962 +#: netbox/dcim/forms/model_forms.py:989 netbox/dcim/forms/model_forms.py:1017 +#: netbox/dcim/forms/model_forms.py:1048 netbox/dcim/forms/model_forms.py:1067 +#: netbox/dcim/forms/model_forms.py:1085 +#: netbox/dcim/forms/object_create.py:123 netbox/dcim/tables/devicetypes.py:82 +#: netbox/templates/dcim/device.html:88 #: netbox/templates/dcim/devicebay.html:52 #: netbox/templates/dcim/module.html:61 msgid "Device Type" msgstr "设备型号" #: netbox/dcim/forms/bulk_edit.py:601 netbox/dcim/forms/model_forms.py:410 -#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66 -#: netbox/templates/dcim/module.html:65 +#: netbox/dcim/forms/model_forms.py:921 netbox/dcim/forms/model_forms.py:963 +#: netbox/dcim/forms/model_forms.py:990 netbox/dcim/forms/model_forms.py:1018 +#: netbox/dcim/forms/model_forms.py:1049 netbox/dcim/forms/model_forms.py:1068 +#: netbox/dcim/forms/model_forms.py:1086 +#: netbox/dcim/forms/object_create.py:124 netbox/dcim/tables/modules.py:17 +#: netbox/dcim/tables/modules.py:66 netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/moduletype.html:24 msgid "Module Type" @@ -4394,8 +4403,8 @@ msgid "Allocated power draw (watts)" msgstr "分配功率(瓦)" #: netbox/dcim/forms/bulk_edit.py:1096 netbox/dcim/forms/bulk_import.py:813 -#: netbox/dcim/forms/model_forms.py:972 netbox/dcim/forms/model_forms.py:1301 -#: netbox/dcim/forms/model_forms.py:1616 netbox/dcim/forms/object_import.py:55 +#: netbox/dcim/forms/model_forms.py:978 netbox/dcim/forms/model_forms.py:1332 +#: netbox/dcim/forms/model_forms.py:1647 netbox/dcim/forms/object_import.py:55 msgid "Power port" msgstr "电源接口" @@ -4429,7 +4438,7 @@ msgid "Wireless role" msgstr "无线角色" #: netbox/dcim/forms/bulk_edit.py:1306 netbox/dcim/forms/model_forms.py:680 -#: netbox/dcim/forms/model_forms.py:1246 netbox/dcim/tables/devices.py:322 +#: netbox/dcim/forms/model_forms.py:1277 netbox/dcim/tables/devices.py:322 #: netbox/templates/dcim/consoleport.html:24 #: netbox/templates/dcim/consoleserverport.html:24 #: netbox/templates/dcim/frontport.html:24 @@ -4448,7 +4457,7 @@ msgstr "模块" msgid "LAG" msgstr "链路聚合" -#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1328 +#: netbox/dcim/forms/bulk_edit.py:1450 netbox/dcim/forms/model_forms.py:1359 msgid "Virtual device contexts" msgstr "设备虚拟上下文" @@ -4476,21 +4485,21 @@ msgstr "速率" msgid "Mode" msgstr "模式" -#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1377 +#: netbox/dcim/forms/bulk_edit.py:1493 netbox/dcim/forms/model_forms.py:1408 #: netbox/ipam/forms/bulk_import.py:174 netbox/ipam/forms/filtersets.py:553 #: netbox/ipam/models/vlans.py:87 netbox/virtualization/forms/bulk_edit.py:222 #: netbox/virtualization/forms/model_forms.py:335 msgid "VLAN group" msgstr "VLAN 组" -#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1383 +#: netbox/dcim/forms/bulk_edit.py:1502 netbox/dcim/forms/model_forms.py:1414 #: netbox/dcim/tables/devices.py:592 #: netbox/virtualization/forms/bulk_edit.py:230 #: netbox/virtualization/forms/model_forms.py:340 msgid "Untagged VLAN" msgstr "未标记的VLAN" -#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1392 +#: netbox/dcim/forms/bulk_edit.py:1511 netbox/dcim/forms/model_forms.py:1423 #: netbox/dcim/tables/devices.py:598 #: netbox/virtualization/forms/bulk_edit.py:238 #: netbox/virtualization/forms/model_forms.py:349 @@ -4505,16 +4514,16 @@ msgstr "添加带标签的 VLAN" msgid "Remove tagged VLANs" msgstr "移除带标签的 VLAN" -#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1401 +#: netbox/dcim/forms/bulk_edit.py:1534 netbox/dcim/forms/model_forms.py:1432 #: netbox/virtualization/forms/model_forms.py:358 msgid "Q-in-Q Service VLAN" msgstr "Q-in-Q 服务 VLAN" -#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1364 +#: netbox/dcim/forms/bulk_edit.py:1549 netbox/dcim/forms/model_forms.py:1395 msgid "Wireless LAN group" msgstr "无线局域网组" -#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1369 +#: netbox/dcim/forms/bulk_edit.py:1554 netbox/dcim/forms/model_forms.py:1400 #: netbox/dcim/tables/devices.py:640 netbox/netbox/navigation/menu.py:152 #: netbox/templates/dcim/interface.html:337 #: netbox/wireless/tables/wirelesslan.py:24 @@ -4522,29 +4531,29 @@ msgid "Wireless LANs" msgstr "无线局域网" #: netbox/dcim/forms/bulk_edit.py:1563 netbox/dcim/forms/filtersets.py:1381 -#: netbox/dcim/forms/model_forms.py:1435 netbox/ipam/forms/bulk_edit.py:269 +#: netbox/dcim/forms/model_forms.py:1466 netbox/ipam/forms/bulk_edit.py:269 #: netbox/ipam/forms/bulk_edit.py:362 netbox/ipam/forms/filtersets.py:177 #: netbox/netbox/navigation/menu.py:108 #: netbox/templates/dcim/interface.html:128 #: netbox/templates/ipam/prefix.html:91 -#: netbox/templates/virtualization/vminterface.html:70 +#: netbox/templates/virtualization/vminterface.html:76 #: netbox/virtualization/forms/filtersets.py:205 #: netbox/virtualization/forms/model_forms.py:378 msgid "Addressing" msgstr "寻址" #: netbox/dcim/forms/bulk_edit.py:1564 netbox/dcim/forms/filtersets.py:721 -#: netbox/dcim/forms/model_forms.py:1436 +#: netbox/dcim/forms/model_forms.py:1467 #: netbox/virtualization/forms/model_forms.py:379 msgid "Operation" msgstr "操作" #: netbox/dcim/forms/bulk_edit.py:1565 netbox/dcim/forms/filtersets.py:1382 -#: netbox/dcim/forms/model_forms.py:1006 netbox/dcim/forms/model_forms.py:1438 +#: netbox/dcim/forms/model_forms.py:1022 netbox/dcim/forms/model_forms.py:1469 msgid "PoE" msgstr "PoE" -#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1437 +#: netbox/dcim/forms/bulk_edit.py:1566 netbox/dcim/forms/model_forms.py:1468 #: netbox/templates/dcim/interface.html:105 #: netbox/virtualization/forms/bulk_edit.py:254 #: netbox/virtualization/forms/model_forms.py:380 @@ -4552,7 +4561,7 @@ msgid "Related Interfaces" msgstr "相关接口" #: netbox/dcim/forms/bulk_edit.py:1568 netbox/dcim/forms/filtersets.py:1383 -#: netbox/dcim/forms/model_forms.py:1441 +#: netbox/dcim/forms/model_forms.py:1472 #: netbox/virtualization/forms/bulk_edit.py:257 #: netbox/virtualization/forms/filtersets.py:206 #: netbox/virtualization/forms/model_forms.py:383 @@ -4810,13 +4819,13 @@ msgstr "该插座供电的电源端口" msgid "Electrical phase (for three-phase circuits)" msgstr "供电相位(用于三相电)" -#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1339 +#: netbox/dcim/forms/bulk_import.py:867 netbox/dcim/forms/model_forms.py:1370 #: netbox/virtualization/forms/bulk_import.py:161 #: netbox/virtualization/forms/model_forms.py:319 msgid "Parent interface" msgstr "上一级接口" -#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1347 +#: netbox/dcim/forms/bulk_import.py:874 netbox/dcim/forms/model_forms.py:1378 #: netbox/virtualization/forms/bulk_import.py:168 #: netbox/virtualization/forms/model_forms.py:327 msgid "Bridged interface" @@ -4880,8 +4889,8 @@ msgstr "无线角色(AP/基站)" msgid "VDC {vdc} is not assigned to device {device}" msgstr "VDC {vdc} 没有指定给设备 {device}" -#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1020 -#: netbox/dcim/forms/model_forms.py:1624 +#: netbox/dcim/forms/bulk_import.py:981 netbox/dcim/forms/model_forms.py:1036 +#: netbox/dcim/forms/model_forms.py:1655 #: netbox/dcim/forms/object_import.py:117 msgid "Rear port" msgstr "后置端口" @@ -5058,7 +5067,7 @@ msgstr "供应类型(AC/DC)" msgid "Single or three-phase" msgstr "单相或三相" -#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1722 +#: netbox/dcim/forms/bulk_import.py:1576 netbox/dcim/forms/model_forms.py:1753 #: netbox/templates/dcim/device.html:190 #: netbox/templates/dcim/virtualdevicecontext.html:30 #: netbox/templates/virtualization/virtualmachine.html:52 @@ -5069,7 +5078,7 @@ msgstr "主 IPv4" msgid "IPv4 address with mask, e.g. 1.2.3.4/24" msgstr "带掩码的 IPv4 地址,例如 1.2.3.4/24" -#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1731 +#: netbox/dcim/forms/bulk_import.py:1583 netbox/dcim/forms/model_forms.py:1762 #: netbox/templates/dcim/device.html:206 #: netbox/templates/dcim/virtualdevicecontext.html:41 #: netbox/templates/virtualization/virtualmachine.html:68 @@ -5230,7 +5239,7 @@ msgstr "类型" msgid "Mgmt only" msgstr "仅用于管理" -#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1423 +#: netbox/dcim/forms/filtersets.py:1443 netbox/dcim/forms/model_forms.py:1454 #: netbox/dcim/models/device_components.py:680 #: netbox/templates/dcim/interface.html:142 msgid "WWN" @@ -5372,7 +5381,7 @@ msgstr "自动填充与此模块类型关联的组件" msgid "Characteristics" msgstr "特性" -#: netbox/dcim/forms/model_forms.py:926 +#: netbox/dcim/forms/model_forms.py:936 #, python-brace-format msgid "" "Alphanumeric ranges are supported for bulk creation. Mixed cases and types " @@ -5384,35 +5393,35 @@ msgstr "" "批量创建支持字母数字范围。不支持单个范围内的混合大小写和类型(例如: [ge,xe] -0/0/ [0-9])。代币 " "{module},如果存在,将在创建新模块时自动替换为位置值。" -#: netbox/dcim/forms/model_forms.py:1107 +#: netbox/dcim/forms/model_forms.py:1138 msgid "Console port template" msgstr "控制台端口模板" -#: netbox/dcim/forms/model_forms.py:1115 +#: netbox/dcim/forms/model_forms.py:1146 msgid "Console server port template" msgstr "控制口模版" -#: netbox/dcim/forms/model_forms.py:1123 +#: netbox/dcim/forms/model_forms.py:1154 msgid "Front port template" msgstr "前向端口模版" -#: netbox/dcim/forms/model_forms.py:1131 +#: netbox/dcim/forms/model_forms.py:1162 msgid "Interface template" msgstr "接口模版" -#: netbox/dcim/forms/model_forms.py:1139 +#: netbox/dcim/forms/model_forms.py:1170 msgid "Power outlet template" msgstr "电源插座模版" -#: netbox/dcim/forms/model_forms.py:1147 +#: netbox/dcim/forms/model_forms.py:1178 msgid "Power port template" msgstr "电源接口模版" -#: netbox/dcim/forms/model_forms.py:1155 +#: netbox/dcim/forms/model_forms.py:1186 msgid "Rear port template" msgstr "后置接口模版" -#: netbox/dcim/forms/model_forms.py:1165 netbox/dcim/forms/model_forms.py:1636 +#: netbox/dcim/forms/model_forms.py:1196 netbox/dcim/forms/model_forms.py:1667 #: netbox/dcim/tables/connections.py:27 #: netbox/templates/dcim/consoleport.html:17 #: netbox/templates/dcim/consoleserverport.html:74 @@ -5420,14 +5429,14 @@ msgstr "后置接口模版" msgid "Console Port" msgstr "Console 端口" -#: netbox/dcim/forms/model_forms.py:1166 netbox/dcim/forms/model_forms.py:1637 +#: netbox/dcim/forms/model_forms.py:1197 netbox/dcim/forms/model_forms.py:1668 #: netbox/templates/dcim/consoleport.html:73 #: netbox/templates/dcim/consoleserverport.html:17 #: netbox/templates/dcim/frontport.html:109 msgid "Console Server Port" msgstr "Console 服务器端口" -#: netbox/dcim/forms/model_forms.py:1167 netbox/dcim/forms/model_forms.py:1638 +#: netbox/dcim/forms/model_forms.py:1198 netbox/dcim/forms/model_forms.py:1669 #: netbox/templates/circuits/inc/circuit_termination_fields.html:53 #: netbox/templates/dcim/consoleport.html:76 #: netbox/templates/dcim/consoleserverport.html:77 @@ -5438,7 +5447,7 @@ msgstr "Console 服务器端口" msgid "Front Port" msgstr "前置接口" -#: netbox/dcim/forms/model_forms.py:1168 netbox/dcim/forms/model_forms.py:1639 +#: netbox/dcim/forms/model_forms.py:1199 netbox/dcim/forms/model_forms.py:1670 #: netbox/dcim/tables/devices.py:743 #: netbox/templates/circuits/inc/circuit_termination_fields.html:54 #: netbox/templates/dcim/consoleport.html:79 @@ -5451,76 +5460,76 @@ msgstr "前置接口" msgid "Rear Port" msgstr "后置接口" -#: netbox/dcim/forms/model_forms.py:1169 netbox/dcim/forms/model_forms.py:1640 +#: netbox/dcim/forms/model_forms.py:1200 netbox/dcim/forms/model_forms.py:1671 #: netbox/dcim/tables/connections.py:46 netbox/dcim/tables/devices.py:520 #: netbox/templates/dcim/poweroutlet.html:54 #: netbox/templates/dcim/powerport.html:17 msgid "Power Port" msgstr "电源接口" -#: netbox/dcim/forms/model_forms.py:1170 netbox/dcim/forms/model_forms.py:1641 +#: netbox/dcim/forms/model_forms.py:1201 netbox/dcim/forms/model_forms.py:1672 #: netbox/templates/dcim/poweroutlet.html:17 #: netbox/templates/dcim/powerport.html:77 msgid "Power Outlet" msgstr "电源插座" -#: netbox/dcim/forms/model_forms.py:1172 netbox/dcim/forms/model_forms.py:1643 +#: netbox/dcim/forms/model_forms.py:1203 netbox/dcim/forms/model_forms.py:1674 msgid "Component Assignment" msgstr "组件分配" -#: netbox/dcim/forms/model_forms.py:1218 netbox/dcim/forms/model_forms.py:1690 +#: netbox/dcim/forms/model_forms.py:1249 netbox/dcim/forms/model_forms.py:1721 msgid "An InventoryItem can only be assigned to a single component." msgstr "库存项只能分配给单个组件" -#: netbox/dcim/forms/model_forms.py:1355 +#: netbox/dcim/forms/model_forms.py:1386 msgid "LAG interface" msgstr "链路聚合接口" -#: netbox/dcim/forms/model_forms.py:1378 +#: netbox/dcim/forms/model_forms.py:1409 msgid "Filter VLANs available for assignment by group." msgstr "按组筛选可供分配的 VLAN。" -#: netbox/dcim/forms/model_forms.py:1533 +#: netbox/dcim/forms/model_forms.py:1564 msgid "Child Device" msgstr "子设备" -#: netbox/dcim/forms/model_forms.py:1534 +#: netbox/dcim/forms/model_forms.py:1565 msgid "" "Child devices must first be created and assigned to the site and rack of the" " parent device." msgstr "必须首先创建子设备,并将其分配给父设备的站点和机柜。" -#: netbox/dcim/forms/model_forms.py:1576 +#: netbox/dcim/forms/model_forms.py:1607 msgid "Console port" msgstr "Console 接口" -#: netbox/dcim/forms/model_forms.py:1584 +#: netbox/dcim/forms/model_forms.py:1615 msgid "Console server port" msgstr "Console 服务器端口" -#: netbox/dcim/forms/model_forms.py:1592 +#: netbox/dcim/forms/model_forms.py:1623 msgid "Front port" msgstr "前置接口" -#: netbox/dcim/forms/model_forms.py:1608 +#: netbox/dcim/forms/model_forms.py:1639 msgid "Power outlet" msgstr "电源插座" -#: netbox/dcim/forms/model_forms.py:1630 +#: netbox/dcim/forms/model_forms.py:1661 #: netbox/templates/dcim/inventoryitem.html:17 msgid "Inventory Item" msgstr "库存项" -#: netbox/dcim/forms/model_forms.py:1704 +#: netbox/dcim/forms/model_forms.py:1735 #: netbox/templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "库存物品分类" -#: netbox/dcim/forms/model_forms.py:1773 +#: netbox/dcim/forms/model_forms.py:1804 msgid "VM Interface" msgstr "虚拟机接口" -#: netbox/dcim/forms/model_forms.py:1788 netbox/ipam/forms/filtersets.py:623 +#: netbox/dcim/forms/model_forms.py:1819 netbox/ipam/forms/filtersets.py:623 #: netbox/ipam/forms/model_forms.py:334 netbox/ipam/forms/model_forms.py:795 #: netbox/ipam/forms/model_forms.py:821 netbox/ipam/tables/vlans.py:171 #: netbox/templates/virtualization/virtualdisk.html:21 @@ -5538,13 +5547,13 @@ msgstr "虚拟机接口" msgid "Virtual Machine" msgstr "虚拟机" -#: netbox/dcim/forms/model_forms.py:1827 +#: netbox/dcim/forms/model_forms.py:1858 msgid "A MAC address can only be assigned to a single object." msgstr "MAC 地址只能分配给单个对象。" #: netbox/dcim/forms/object_create.py:48 -#: netbox/dcim/forms/object_create.py:204 -#: netbox/dcim/forms/object_create.py:353 +#: netbox/dcim/forms/object_create.py:210 +#: netbox/dcim/forms/object_create.py:359 msgid "" "Alphanumeric ranges are supported. (Must match the number of objects being " "created.)" @@ -5558,47 +5567,47 @@ msgid "" msgstr "提供了 {value_count}个参数,实际需要{pattern_count}个。" #: netbox/dcim/forms/object_create.py:114 -#: netbox/dcim/forms/object_create.py:268 netbox/dcim/tables/devices.py:262 +#: netbox/dcim/forms/object_create.py:274 netbox/dcim/tables/devices.py:262 msgid "Rear ports" msgstr "后置接口" #: netbox/dcim/forms/object_create.py:115 -#: netbox/dcim/forms/object_create.py:269 +#: netbox/dcim/forms/object_create.py:275 msgid "Select one rear port assignment for each front port being created." msgstr "为正在创建的每个前置接口指定一个后置接口" -#: netbox/dcim/forms/object_create.py:169 +#: netbox/dcim/forms/object_create.py:175 #, python-brace-format msgid "" "The number of front port templates to be created ({frontport_count}) must " "match the selected number of rear port positions ({rearport_count})." msgstr "要创建的前置端口数({frontport_count}) 必须与所选的后置端口数({rearport_count})匹配。" -#: netbox/dcim/forms/object_create.py:318 +#: netbox/dcim/forms/object_create.py:324 #, python-brace-format msgid "" "The number of front ports to be created ({frontport_count}) must match the " "selected number of rear port positions ({rearport_count})." msgstr "要创建的前置端口数 ({frontport_count}) 必须与所选的后置端口数({rearport_count})匹配。" -#: netbox/dcim/forms/object_create.py:407 netbox/dcim/tables/devices.py:1064 +#: netbox/dcim/forms/object_create.py:413 netbox/dcim/tables/devices.py:1064 #: netbox/ipam/tables/fhrp.py:31 netbox/templates/dcim/virtualchassis.html:53 #: netbox/templates/dcim/virtualchassis_edit.html:51 #: netbox/templates/ipam/fhrpgroup.html:38 msgid "Members" msgstr "成员" -#: netbox/dcim/forms/object_create.py:417 +#: netbox/dcim/forms/object_create.py:423 msgid "Initial position" msgstr "初始位置" -#: netbox/dcim/forms/object_create.py:420 +#: netbox/dcim/forms/object_create.py:426 msgid "" "Position of the first member device. Increases by one for each additional " "member." msgstr "第一个成员设备的位置。每增加一个成员增加一个。" -#: netbox/dcim/forms/object_create.py:435 +#: netbox/dcim/forms/object_create.py:441 msgid "A position must be specified for the first VC member." msgstr "必须为第一个VC成员指定一个位置。" @@ -6060,6 +6069,7 @@ msgstr "已标记 VLANs" #: netbox/ipam/forms/bulk_import.py:507 netbox/ipam/forms/filtersets.py:579 #: netbox/ipam/forms/model_forms.py:691 netbox/ipam/tables/vlans.py:106 #: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77 +#: netbox/templates/virtualization/vminterface.html:60 msgid "Q-in-Q SVLAN" msgstr "Q-in-Q SVLAN" @@ -7214,8 +7224,8 @@ msgid "Power outlets" msgstr "电源插座" #: netbox/dcim/tables/devices.py:256 netbox/dcim/tables/devices.py:1112 -#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1203 -#: netbox/dcim/views.py:1447 netbox/dcim/views.py:2200 +#: netbox/dcim/tables/devicetypes.py:133 netbox/dcim/views.py:1204 +#: netbox/dcim/views.py:1448 netbox/dcim/views.py:2201 #: netbox/netbox/navigation/menu.py:94 netbox/netbox/navigation/menu.py:258 #: netbox/templates/dcim/device/base.html:37 #: netbox/templates/dcim/device_list.html:43 @@ -7253,8 +7263,8 @@ msgid "Module Bay" msgstr "设备板卡插槽" #: netbox/dcim/tables/devices.py:327 netbox/dcim/tables/devicetypes.py:52 -#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1278 -#: netbox/dcim/views.py:2298 netbox/netbox/navigation/menu.py:103 +#: netbox/dcim/tables/devicetypes.py:148 netbox/dcim/views.py:1279 +#: netbox/dcim/views.py:2299 netbox/netbox/navigation/menu.py:103 #: netbox/templates/dcim/device/base.html:52 #: netbox/templates/dcim/device_list.html:71 #: netbox/templates/dcim/devicetype/base.html:49 @@ -7290,7 +7300,7 @@ msgstr "分配功率(W)" #: netbox/templates/dcim/interface.html:396 #: netbox/templates/ipam/ipaddress_bulk_add.html:15 #: netbox/templates/ipam/service.html:40 -#: netbox/templates/virtualization/vminterface.html:101 +#: netbox/templates/virtualization/vminterface.html:107 #: netbox/vpn/tables/tunnels.py:98 msgid "IP Addresses" msgstr "IP地址" @@ -7301,7 +7311,7 @@ msgid "FHRP Groups" msgstr "网关冗余协议组" #: netbox/dcim/tables/devices.py:589 netbox/templates/dcim/interface.html:95 -#: netbox/templates/virtualization/vminterface.html:59 +#: netbox/templates/virtualization/vminterface.html:65 #: netbox/templates/vpn/tunnel.html:18 #: netbox/templates/vpn/tunneltermination.html:13 #: netbox/vpn/forms/bulk_edit.py:76 netbox/vpn/forms/bulk_import.py:76 @@ -7388,8 +7398,8 @@ msgstr "U高度" msgid "Instances" msgstr "实例" -#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1143 -#: netbox/dcim/views.py:1387 netbox/dcim/views.py:2136 +#: netbox/dcim/tables/devicetypes.py:121 netbox/dcim/views.py:1144 +#: netbox/dcim/views.py:1388 netbox/dcim/views.py:2137 #: netbox/netbox/navigation/menu.py:97 #: netbox/templates/dcim/device/base.html:25 #: netbox/templates/dcim/device_list.html:15 @@ -7399,8 +7409,8 @@ msgstr "实例" msgid "Console Ports" msgstr "Console口" -#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1158 -#: netbox/dcim/views.py:1402 netbox/dcim/views.py:2152 +#: netbox/dcim/tables/devicetypes.py:124 netbox/dcim/views.py:1159 +#: netbox/dcim/views.py:1403 netbox/dcim/views.py:2153 #: netbox/netbox/navigation/menu.py:98 #: netbox/templates/dcim/device/base.html:28 #: netbox/templates/dcim/device_list.html:22 @@ -7410,8 +7420,8 @@ msgstr "Console口" msgid "Console Server Ports" msgstr "Console 服务端口" -#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1173 -#: netbox/dcim/views.py:1417 netbox/dcim/views.py:2168 +#: netbox/dcim/tables/devicetypes.py:127 netbox/dcim/views.py:1174 +#: netbox/dcim/views.py:1418 netbox/dcim/views.py:2169 #: netbox/netbox/navigation/menu.py:99 #: netbox/templates/dcim/device/base.html:31 #: netbox/templates/dcim/device_list.html:29 @@ -7421,8 +7431,8 @@ msgstr "Console 服务端口" msgid "Power Ports" msgstr "电源接口" -#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1188 -#: netbox/dcim/views.py:1432 netbox/dcim/views.py:2184 +#: netbox/dcim/tables/devicetypes.py:130 netbox/dcim/views.py:1189 +#: netbox/dcim/views.py:1433 netbox/dcim/views.py:2185 #: netbox/netbox/navigation/menu.py:100 #: netbox/templates/dcim/device/base.html:34 #: netbox/templates/dcim/device_list.html:36 @@ -7432,8 +7442,8 @@ msgstr "电源接口" msgid "Power Outlets" msgstr "PDU" -#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1218 -#: netbox/dcim/views.py:1462 netbox/dcim/views.py:2222 +#: netbox/dcim/tables/devicetypes.py:136 netbox/dcim/views.py:1219 +#: netbox/dcim/views.py:1463 netbox/dcim/views.py:2223 #: netbox/netbox/navigation/menu.py:95 #: netbox/templates/dcim/device/base.html:40 #: netbox/templates/dcim/devicetype/base.html:37 @@ -7442,8 +7452,8 @@ msgstr "PDU" msgid "Front Ports" msgstr "前置端口" -#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1233 -#: netbox/dcim/views.py:1477 netbox/dcim/views.py:2238 +#: netbox/dcim/tables/devicetypes.py:139 netbox/dcim/views.py:1234 +#: netbox/dcim/views.py:1478 netbox/dcim/views.py:2239 #: netbox/netbox/navigation/menu.py:96 #: netbox/templates/dcim/device/base.html:43 #: netbox/templates/dcim/device_list.html:50 @@ -7453,16 +7463,16 @@ msgstr "前置端口" msgid "Rear Ports" msgstr "后置端口" -#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1263 -#: netbox/dcim/views.py:2278 netbox/netbox/navigation/menu.py:102 +#: netbox/dcim/tables/devicetypes.py:142 netbox/dcim/views.py:1264 +#: netbox/dcim/views.py:2279 netbox/netbox/navigation/menu.py:102 #: netbox/templates/dcim/device/base.html:49 #: netbox/templates/dcim/device_list.html:57 #: netbox/templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "机柜托架" -#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1248 -#: netbox/dcim/views.py:1492 netbox/dcim/views.py:2258 +#: netbox/dcim/tables/devicetypes.py:145 netbox/dcim/views.py:1249 +#: netbox/dcim/views.py:1493 netbox/dcim/views.py:2259 #: netbox/netbox/navigation/menu.py:101 #: netbox/templates/dcim/device/base.html:46 #: netbox/templates/dcim/device_list.html:64 @@ -7531,62 +7541,62 @@ msgstr "VLAN 组" msgid "Test case must set peer_termination_type" msgstr "测试用例必须设置对端端点类型" -#: netbox/dcim/views.py:137 +#: netbox/dcim/views.py:138 #, python-brace-format msgid "Disconnected {count} {type}" msgstr "已断开连接{count} {type}" -#: netbox/dcim/views.py:884 netbox/netbox/navigation/menu.py:51 +#: netbox/dcim/views.py:885 netbox/netbox/navigation/menu.py:51 msgid "Reservations" msgstr "机柜预留" -#: netbox/dcim/views.py:903 netbox/templates/dcim/location.html:90 +#: netbox/dcim/views.py:904 netbox/templates/dcim/location.html:90 #: netbox/templates/dcim/site.html:140 msgid "Non-Racked Devices" msgstr "未上架设备" -#: netbox/dcim/views.py:2311 netbox/extras/forms/model_forms.py:591 +#: netbox/dcim/views.py:2312 netbox/extras/forms/model_forms.py:591 #: netbox/templates/extras/configcontext.html:10 #: netbox/virtualization/forms/model_forms.py:232 #: netbox/virtualization/views.py:446 msgid "Config Context" msgstr "配置实例" -#: netbox/dcim/views.py:2321 netbox/virtualization/views.py:456 +#: netbox/dcim/views.py:2322 netbox/virtualization/views.py:456 msgid "Render Config" msgstr "提交配置" -#: netbox/dcim/views.py:2334 netbox/extras/tables/tables.py:556 +#: netbox/dcim/views.py:2335 netbox/extras/tables/tables.py:556 #: netbox/netbox/navigation/menu.py:255 netbox/netbox/navigation/menu.py:257 #: netbox/virtualization/views.py:214 msgid "Virtual Machines" msgstr "虚拟机" -#: netbox/dcim/views.py:3167 +#: netbox/dcim/views.py:3168 #, python-brace-format msgid "Installed device {device} in bay {device_bay}." msgstr "已安装的设备 {device} 在海湾里 {device_bay}。" -#: netbox/dcim/views.py:3208 +#: netbox/dcim/views.py:3209 #, python-brace-format msgid "Removed device {device} from bay {device_bay}." msgstr "已移除的设备 {device} 来自海湾 {device_bay}。" -#: netbox/dcim/views.py:3324 netbox/ipam/tables/ip.py:180 +#: netbox/dcim/views.py:3325 netbox/ipam/tables/ip.py:180 msgid "Children" msgstr "子网" -#: netbox/dcim/views.py:3791 +#: netbox/dcim/views.py:3792 #, python-brace-format msgid "Added member {device}" msgstr "已添加成员 {device}" -#: netbox/dcim/views.py:3840 +#: netbox/dcim/views.py:3841 #, python-brace-format msgid "Unable to remove master device {device} from the virtual chassis." msgstr "无法移除主设备 {device} 来自虚拟机箱。" -#: netbox/dcim/views.py:3853 +#: netbox/dcim/views.py:3854 #, python-brace-format msgid "Removed {device} from virtual chassis {chassis}" msgstr "已移除 {device} 来自虚拟机箱 {chassis}" @@ -11404,7 +11414,7 @@ msgstr "库存物品分类" #: netbox/netbox/navigation/menu.py:110 #: netbox/templates/dcim/interface.html:413 -#: netbox/templates/virtualization/vminterface.html:118 +#: netbox/templates/virtualization/vminterface.html:124 msgid "MAC Addresses" msgstr "MAC 地址" @@ -11919,31 +11929,31 @@ msgstr "值" msgid "Dummy Plugin" msgstr "虚拟插件" -#: netbox/netbox/views/generic/bulk_views.py:115 +#: netbox/netbox/views/generic/bulk_views.py:116 #, python-brace-format msgid "" "There was an error rendering the selected export template ({template}): " "{error}" msgstr "渲染所选导出模板时出错 ({template}): {error}" -#: netbox/netbox/views/generic/bulk_views.py:421 +#: netbox/netbox/views/generic/bulk_views.py:425 #, python-brace-format msgid "Row {i}: Object with ID {id} does not exist" msgstr "第{i}行: ID为{id}的对象不存在" -#: netbox/netbox/views/generic/bulk_views.py:710 -#: netbox/netbox/views/generic/bulk_views.py:911 -#: netbox/netbox/views/generic/bulk_views.py:959 +#: netbox/netbox/views/generic/bulk_views.py:714 +#: netbox/netbox/views/generic/bulk_views.py:915 +#: netbox/netbox/views/generic/bulk_views.py:963 #, python-brace-format msgid "No {object_type} were selected." msgstr "没有 {object_type} 被选中。" -#: netbox/netbox/views/generic/bulk_views.py:789 +#: netbox/netbox/views/generic/bulk_views.py:793 #, python-brace-format msgid "Renamed {count} {object_type}" msgstr "重命名 {count} {object_type}" -#: netbox/netbox/views/generic/bulk_views.py:889 +#: netbox/netbox/views/generic/bulk_views.py:893 #, python-brace-format msgid "Deleted {count} {object_type}" msgstr "已删除 {count} {object_type}" @@ -11970,7 +11980,7 @@ msgstr "的同步数据 {object_type} {object}。" msgid "Synced {count} {object_type}" msgstr "已同步 {count} {object_type}" -#: netbox/netbox/views/generic/object_views.py:108 +#: netbox/netbox/views/generic/object_views.py:109 #, python-brace-format msgid "{class_name} must implement get_children()" msgstr "{class_name}必须实现get_children()方法" @@ -12243,32 +12253,36 @@ msgstr "NetBox 图案" msgid "NetBox Logo" msgstr "NetBox Logo" -#: netbox/templates/base/layout.html:150 netbox/templates/base/layout.html:151 +#: netbox/templates/base/layout.html:60 netbox/templates/base/layout.html:61 +msgid "Get" +msgstr "" + +#: netbox/templates/base/layout.html:161 netbox/templates/base/layout.html:162 msgid "Docs" msgstr "文档" -#: netbox/templates/base/layout.html:156 netbox/templates/base/layout.html:157 +#: netbox/templates/base/layout.html:167 netbox/templates/base/layout.html:168 #: netbox/templates/rest_framework/api.html:10 msgid "REST API" msgstr "REST API" -#: netbox/templates/base/layout.html:162 netbox/templates/base/layout.html:163 +#: netbox/templates/base/layout.html:173 netbox/templates/base/layout.html:174 msgid "REST API documentation" msgstr "REST API 文档" -#: netbox/templates/base/layout.html:169 netbox/templates/base/layout.html:170 +#: netbox/templates/base/layout.html:180 netbox/templates/base/layout.html:181 msgid "GraphQL API" msgstr "GraphQL API" -#: netbox/templates/base/layout.html:185 netbox/templates/base/layout.html:186 +#: netbox/templates/base/layout.html:196 netbox/templates/base/layout.html:197 msgid "NetBox Labs Support" msgstr "NetBox 实验室支持" -#: netbox/templates/base/layout.html:194 netbox/templates/base/layout.html:195 +#: netbox/templates/base/layout.html:205 netbox/templates/base/layout.html:206 msgid "Source Code" msgstr "源代码" -#: netbox/templates/base/layout.html:200 netbox/templates/base/layout.html:201 +#: netbox/templates/base/layout.html:211 netbox/templates/base/layout.html:212 msgid "Community" msgstr "社区" @@ -13274,7 +13288,7 @@ msgid "PoE Type" msgstr "PoE类型" #: netbox/templates/dcim/interface.html:156 -#: netbox/templates/virtualization/vminterface.html:88 +#: netbox/templates/virtualization/vminterface.html:94 msgid "VLAN Translation" msgstr "VLAN 转换" @@ -13327,12 +13341,12 @@ msgstr "无成员接口" #: netbox/templates/ipam/fhrpgroup.html:73 #: netbox/templates/ipam/iprange/ip_addresses.html:7 #: netbox/templates/ipam/prefix/ip_addresses.html:7 -#: netbox/templates/virtualization/vminterface.html:105 +#: netbox/templates/virtualization/vminterface.html:111 msgid "Add IP Address" msgstr "增加 IP 地址" #: netbox/templates/dcim/interface.html:417 -#: netbox/templates/virtualization/vminterface.html:123 +#: netbox/templates/virtualization/vminterface.html:129 msgid "Add MAC Address" msgstr "添加 MAC 地址" @@ -15624,7 +15638,7 @@ msgstr "无效的权限名称: {name}. 格式必须是 ._ content embedded by plugins + """ + return _get_registered_content(None, 'head', context) + + @register.simple_tag(takes_context=True) def plugin_navbar(context): """ diff --git a/netbox/utilities/testing/filtersets.py b/netbox/utilities/testing/filtersets.py index e58123f03..0b3d4b198 100644 --- a/netbox/utilities/testing/filtersets.py +++ b/netbox/utilities/testing/filtersets.py @@ -144,8 +144,8 @@ class BaseFilterSetTests: # Check that the filter class is correct filter = filters[filter_name] if filter_class is not None: - self.assertIs( - type(filter), + self.assertIsInstance( + filter, filter_class, f"Invalid filter class {type(filter)} for {filter_name} (should be {filter_class})!" ) diff --git a/netbox/utilities/tests/test_filters.py b/netbox/utilities/tests/test_filters.py index 6956396d2..1598d3d52 100644 --- a/netbox/utilities/tests/test_filters.py +++ b/netbox/utilities/tests/test_filters.py @@ -391,7 +391,8 @@ class DynamicFilterLookupExpressionTest(TestCase): DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() platforms = ( Platform(name='Platform 1', slug='platform-1'), diff --git a/netbox/utilities/validators.py b/netbox/utilities/validators.py index 0e896e52a..4b7529472 100644 --- a/netbox/utilities/validators.py +++ b/netbox/utilities/validators.py @@ -1,3 +1,4 @@ +import decimal import re from django.core.exceptions import ValidationError @@ -10,6 +11,7 @@ __all__ = ( 'ColorValidator', 'EnhancedURLValidator', 'ExclusionValidator', + 'MultipleOfValidator', 'validate_regex', ) @@ -54,6 +56,22 @@ class ExclusionValidator(BaseValidator): return a in b +class MultipleOfValidator(BaseValidator): + """ + Checks that a field's value is a numeric multiple of the given value. Both values are + cast as Decimals for comparison. + """ + def __init__(self, multiple): + self.multiple = decimal.Decimal(str(multiple)) + super().__init__(limit_value=None) + + def __call__(self, value): + if decimal.Decimal(str(value)) % self.multiple != 0: + raise ValidationError( + _("{value} must be a multiple of {multiple}.").format(value=value, multiple=self.multiple) + ) + + def validate_regex(value): """ Checks that the value is a valid regular expression. (Don't confuse this with RegexValidator, which *uses* a regex diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index b8f65baf1..1263874c4 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -208,22 +208,30 @@ class ViewTab: Args: label: Human-friendly text + visible: A callable which determines whether the tab should be displayed. This callable must accept exactly one + argument: the object instance. If a callable is not specified, the tab's visibility will be determined by + its badge (if any) and the value of `hide_if_empty`. badge: A static value or callable to display alongside the label (optional). If a callable is used, it must accept a single argument representing the object being viewed. weight: Numeric weight to influence ordering among other tabs (default: 1000) permission: The permission required to display the tab (optional). - hide_if_empty: If true, the tab will be displayed only if its badge has a meaningful value. (Tabs without a - badge are always displayed.) + hide_if_empty: If true, the tab will be displayed only if its badge has a meaningful value. (This parameter is + evaluated only if the tab is permitted to be displayed according to the `visible` parameter.) """ - def __init__(self, label, badge=None, weight=1000, permission=None, hide_if_empty=False): + def __init__(self, label, visible=None, badge=None, weight=1000, permission=None, hide_if_empty=False): self.label = label + self.visible = visible self.badge = badge self.weight = weight self.permission = permission self.hide_if_empty = hide_if_empty def render(self, instance): - """Return the attributes needed to render a tab in HTML.""" + """ + Return the attributes needed to render a tab in HTML if the tab should be displayed. Otherwise, return None. + """ + if self.visible is not None and not self.visible(instance): + return None badge_value = self._get_badge_value(instance) if self.badge and self.hide_if_empty and not badge_value: return None diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py index b031d2bf3..06a38da36 100644 --- a/netbox/virtualization/filtersets.py +++ b/netbox/virtualization/filtersets.py @@ -171,13 +171,15 @@ class VirtualMachineFilterSet( name = MultiValueCharFilter( lookup_expr='iexact' ) - role_id = django_filters.ModelMultipleChoiceFilter( + role_id = TreeNodeMultipleChoiceFilter( queryset=DeviceRole.objects.all(), + lookup_expr='in', label=_('Role (ID)'), ) - role = django_filters.ModelMultipleChoiceFilter( - field_name='role__slug', + role = TreeNodeMultipleChoiceFilter( + field_name='role', queryset=DeviceRole.objects.all(), + lookup_expr='in', to_field_name='slug', label=_('Role (slug)'), ) diff --git a/netbox/virtualization/graphql/enums.py b/netbox/virtualization/graphql/enums.py new file mode 100644 index 000000000..3c7ebad8a --- /dev/null +++ b/netbox/virtualization/graphql/enums.py @@ -0,0 +1,11 @@ +import strawberry + +from virtualization.choices import * + +__all__ = ( + 'ClusterStatusEnum', + 'VirtualMachineStatusEnum', +) + +ClusterStatusEnum = strawberry.enum(ClusterStatusChoices.as_enum(prefix='status')) +VirtualMachineStatusEnum = strawberry.enum(VirtualMachineStatusChoices.as_enum(prefix='status')) diff --git a/netbox/virtualization/graphql/filter_mixins.py b/netbox/virtualization/graphql/filter_mixins.py new file mode 100644 index 000000000..ed7a6cfb1 --- /dev/null +++ b/netbox/virtualization/graphql/filter_mixins.py @@ -0,0 +1,26 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django +from strawberry import ID +from strawberry_django import FilterLookup + +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin + +if TYPE_CHECKING: + from .filters import VirtualMachineFilter + +__all__ = ( + 'VMComponentFilterMixin', +) + + +@dataclass +class VMComponentFilterMixin(NetBoxModelFilterMixin): + virtual_machine: Annotated['VirtualMachineFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_machine_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index 610275d37..2a09e86d1 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -1,7 +1,33 @@ -import strawberry_django +from typing import Annotated, TYPE_CHECKING -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from virtualization import filtersets, models +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from dcim.graphql.filter_mixins import InterfaceBaseFilterMixin, RenderConfigFilterMixin, ScopedFilterMixin +from extras.graphql.filter_mixins import ConfigContextFilterMixin +from netbox.graphql.filter_mixins import ( + ImageAttachmentFilterMixin, + OrganizationalModelFilterMixin, + PrimaryModelFilterMixin, +) +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin +from virtualization import models +from virtualization.graphql.filter_mixins import VMComponentFilterMixin + +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.filter_lookups import FloatLookup, IntegerLookup + from dcim.graphql.filters import DeviceFilter, DeviceRoleFilter, MACAddressFilter, PlatformFilter, SiteFilter + from ipam.graphql.filters import ( + FHRPGroupAssignmentFilter, + IPAddressFilter, + ServiceFilter, + VLANGroupFilter, + VRFFilter, + ) + from vpn.graphql.filters import L2VPNFilter, TunnelTerminationFilter __all__ = ( 'ClusterFilter', @@ -14,36 +40,123 @@ __all__ = ( @strawberry_django.filter(models.Cluster, lookups=True) -@autotype_decorator(filtersets.ClusterFilterSet) -class ClusterFilter(BaseFilterMixin): - pass +class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + type_id: ID | None = strawberry_django.filter_field() + group: Annotated['ClusterGroupFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + status: Annotated['ClusterStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ClusterGroup, lookups=True) -@autotype_decorator(filtersets.ClusterGroupFilterSet) -class ClusterGroupFilter(BaseFilterMixin): - pass +class ClusterGroupFilter(ContactFilterMixin, OrganizationalModelFilterMixin): + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ClusterType, lookups=True) -@autotype_decorator(filtersets.ClusterTypeFilterSet) -class ClusterTypeFilter(BaseFilterMixin): +class ClusterTypeFilter(OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.VirtualMachine, lookups=True) -@autotype_decorator(filtersets.VirtualMachineFilterSet) -class VirtualMachineFilter(BaseFilterMixin): - pass +class VirtualMachineFilter( + ContactFilterMixin, + ImageAttachmentFilterMixin, + RenderConfigFilterMixin, + ConfigContextFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin, +): + name: FilterLookup[str] | 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() + cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_id: ID | 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() + platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + platform_id: ID | None = strawberry_django.filter_field() + status: Annotated['VirtualMachineStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4_id: ID | None = strawberry_django.filter_field() + primary_ip6: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip6_id: ID | None = strawberry_django.filter_field() + vcpus: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + memory: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + disk: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + serial: FilterLookup[str] | None = strawberry_django.filter_field() + interface_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 = ( + strawberry_django.filter_field() + ) + services: Annotated['ServiceFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_disks: Annotated['VirtualDiskFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VMInterface, lookups=True) -@autotype_decorator(filtersets.VMInterfaceFilterSet) -class VMInterfaceFilter(BaseFilterMixin): - pass +class VMInterfaceFilter(VMComponentFilterMixin, InterfaceBaseFilterMixin): + ip_addresses: Annotated['IPAddressFilter', 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() + parent: Annotated['VMInterfaceFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + fhrp_group_assignments: Annotated['FHRPGroupAssignmentFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tunnel_terminations: Annotated['TunnelTerminationFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + mac_addresses: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VirtualDisk, lookups=True) -@autotype_decorator(filtersets.VirtualDiskFilterSet) -class VirtualDiskFilter(BaseFilterMixin): - pass +class VirtualDiskFilter(VMComponentFilterMixin): + size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/virtualization/graphql/types.py b/netbox/virtualization/graphql/types.py index 9b6e11dc2..ba20e6844 100644 --- a/netbox/virtualization/graphql/types.py +++ b/netbox/virtualization/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -10,6 +10,21 @@ from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType from virtualization import models from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import ( + DeviceRoleType, + DeviceType, + LocationType, + MACAddressType, + PlatformType, + RegionType, + SiteGroupType, + SiteType, + ) + from extras.graphql.types import ConfigTemplateType + from ipam.graphql.types import IPAddressType, ServiceType, VLANTranslationPolicyType, VLANType, VRFType + from tenancy.graphql.types import TenantType + __all__ = ( 'ClusterType', 'ClusterGroupType', @@ -30,8 +45,9 @@ class ComponentType(NetBoxObjectType): @strawberry_django.type( models.Cluster, - exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), - filters=ClusterFilter + exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], + filters=ClusterFilter, + pagination=True ) class ClusterType(ContactsMixin, VLANGroupsMixin, NetBoxObjectType): type: Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')] | None @@ -53,7 +69,8 @@ class ClusterType(ContactsMixin, VLANGroupsMixin, NetBoxObjectType): @strawberry_django.type( models.ClusterGroup, fields='__all__', - filters=ClusterGroupFilter + filters=ClusterGroupFilter, + pagination=True ) class ClusterGroupType(ContactsMixin, VLANGroupsMixin, OrganizationalObjectType): @@ -63,7 +80,8 @@ class ClusterGroupType(ContactsMixin, VLANGroupsMixin, OrganizationalObjectType) @strawberry_django.type( models.ClusterType, fields='__all__', - filters=ClusterTypeFilter + filters=ClusterTypeFilter, + pagination=True ) class ClusterTypeType(OrganizationalObjectType): @@ -73,7 +91,8 @@ class ClusterTypeType(OrganizationalObjectType): @strawberry_django.type( models.VirtualMachine, fields='__all__', - filters=VirtualMachineFilter + filters=VirtualMachineFilter, + pagination=True ) class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType): interface_count: BigInt @@ -97,7 +116,8 @@ class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.VMInterface, fields='__all__', - filters=VMInterfaceFilter + filters=VMInterfaceFilter, + pagination=True ) class VMInterfaceType(IPAddressesMixin, ComponentType): _name: str @@ -119,7 +139,8 @@ class VMInterfaceType(IPAddressesMixin, ComponentType): @strawberry_django.type( models.VirtualDisk, fields='__all__', - filters=VirtualDiskFilter + filters=VirtualDiskFilter, + pagination=True ) class VirtualDiskType(ComponentType): pass diff --git a/netbox/virtualization/migrations/0001_squashed_0022.py b/netbox/virtualization/migrations/0001_squashed_0022.py index c7aa35ec7..caa890b13 100644 --- a/netbox/virtualization/migrations/0001_squashed_0022.py +++ b/netbox/virtualization/migrations/0001_squashed_0022.py @@ -13,10 +13,10 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('dcim', '0002_auto_20160622_1821'), - ('ipam', '0001_initial'), - ('extras', '0001_initial'), - ('tenancy', '0001_initial'), + ('dcim', '0002_squashed'), + ('ipam', '0001_squashed'), + ('extras', '0001_squashed'), + ('tenancy', '0001_squashed_0012'), ] replaces = [ @@ -154,7 +154,6 @@ class Migration(migrations.Migration): 'role', models.ForeignKey( blank=True, - limit_choices_to={'vm_role': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', diff --git a/netbox/virtualization/migrations/0023_squashed_0036.py b/netbox/virtualization/migrations/0023_squashed_0036.py index 0665aaab6..6ff8fcae4 100644 --- a/netbox/virtualization/migrations/0023_squashed_0036.py +++ b/netbox/virtualization/migrations/0023_squashed_0036.py @@ -26,9 +26,9 @@ class Migration(migrations.Migration): dependencies = [ ('dcim', '0003_squashed_0130'), - ('extras', '0098_webhook_custom_field_data_webhook_tags'), + ('extras', '0087_squashed_0098'), ('ipam', '0047_squashed_0053'), - ('virtualization', '0022_vminterface_parent'), + ('virtualization', '0001_squashed_0022'), ] operations = [ diff --git a/netbox/virtualization/migrations/0037_protect_child_interfaces.py b/netbox/virtualization/migrations/0037_protect_child_interfaces.py index a9d2075c1..a19e4e9ce 100644 --- a/netbox/virtualization/migrations/0037_protect_child_interfaces.py +++ b/netbox/virtualization/migrations/0037_protect_child_interfaces.py @@ -6,7 +6,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('virtualization', '0036_virtualmachine_config_template'), + ('virtualization', '0023_squashed_0036'), ] operations = [ diff --git a/netbox/virtualization/migrations/0044_cluster_scope.py b/netbox/virtualization/migrations/0044_cluster_scope.py index 521db1877..31dd72989 100644 --- a/netbox/virtualization/migrations/0044_cluster_scope.py +++ b/netbox/virtualization/migrations/0044_cluster_scope.py @@ -32,7 +32,6 @@ class Migration(migrations.Migration): name='scope_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q(('model__in', ('region', 'sitegroup', 'site', 'location'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index fab30c6f2..aca2a7dbd 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/netbox/virtualization/models/virtualmachines.py @@ -82,7 +82,6 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co to='dcim.DeviceRole', on_delete=models.PROTECT, related_name='virtual_machines', - limit_choices_to={'vm_role': True}, blank=True, null=True ) @@ -127,6 +126,12 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co blank=True, max_length=50 ) + services = GenericRelation( + to='ipam.Service', + content_type_field='parent_object_type', + object_id_field='parent_object_id', + related_query_name='virtual_machine', + ) # Counter fields interface_count = CounterCacheField( diff --git a/netbox/virtualization/tests/test_filtersets.py b/netbox/virtualization/tests/test_filtersets.py index 81db07188..228cf66ed 100644 --- a/netbox/virtualization/tests/test_filtersets.py +++ b/netbox/virtualization/tests/test_filtersets.py @@ -294,7 +294,8 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests): DeviceRole(name='Device Role 2', slug='device-role-2'), DeviceRole(name='Device Role 3', slug='device-role-3'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() devices = ( create_test_device('device1', cluster=clusters[0]), diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index 3c8d7eadc..27b1a08a5 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -203,7 +203,8 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase): DeviceRole(name='Device Role 1', slug='device-role-1'), DeviceRole(name='Device Role 2', slug='device-role-2'), ) - DeviceRole.objects.bulk_create(roles) + for role in roles: + role.save() platforms = ( Platform(name='Platform 1', slug='platform-1'), diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 8d17f753c..ba5a1128a 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -15,7 +15,6 @@ from ipam.models import IPAddress, VLANGroup from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.views import generic -from tenancy.views import ObjectContactsView from utilities.query import count_related from utilities.query_functions import CollateAsChar from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view @@ -157,11 +156,6 @@ class ClusterGroupBulkDeleteView(generic.BulkDeleteView): table = tables.ClusterGroupTable -@register_model_view(ClusterGroup, 'contacts') -class ClusterGroupContactsView(ObjectContactsView): - queryset = ClusterGroup.objects.all() - - # # Clusters # @@ -366,11 +360,6 @@ class ClusterRemoveDevicesView(generic.ObjectEditView): }) -@register_model_view(Cluster, 'contacts') -class ClusterContactsView(ObjectContactsView): - queryset = Cluster.objects.all() - - # # Virtual machines # @@ -491,11 +480,6 @@ class VirtualMachineBulkDeleteView(generic.BulkDeleteView): table = tables.VirtualMachineTable -@register_model_view(VirtualMachine, 'contacts') -class VirtualMachineContactsView(ObjectContactsView): - queryset = VirtualMachine.objects.all() - - # # VM interfaces # diff --git a/netbox/vpn/api/serializers_/l2vpn.py b/netbox/vpn/api/serializers_/l2vpn.py index c16cbbe1d..f7c27113a 100644 --- a/netbox/vpn/api/serializers_/l2vpn.py +++ b/netbox/vpn/api/serializers_/l2vpn.py @@ -34,11 +34,12 @@ class L2VPNSerializer(NetBoxModelSerializer): many=True ) tenant = TenantSerializer(nested=True, required=False, allow_null=True) + status = ChoiceField(choices=L2VPNStatusChoices, required=False) class Meta: model = L2VPN fields = [ - 'id', 'url', 'display_url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', + 'id', 'url', 'display_url', 'display', 'identifier', 'name', 'slug', 'type', 'status', 'import_targets', 'export_targets', 'description', 'comments', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated' ] brief_fields = ('id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'description') diff --git a/netbox/vpn/choices.py b/netbox/vpn/choices.py index 9847e1b97..db03e48f8 100644 --- a/netbox/vpn/choices.py +++ b/netbox/vpn/choices.py @@ -228,6 +228,7 @@ class L2VPNTypeChoices(ChoiceSet): TYPE_MPLS_EVPN = 'mpls-evpn' TYPE_PBB_EVPN = 'pbb-evpn' TYPE_EVPN_VPWS = 'evpn-vpws' + TYPE_SPB = 'spb' CHOICES = ( ('VPLS', ( @@ -255,6 +256,9 @@ class L2VPNTypeChoices(ChoiceSet): (TYPE_EPTREE, _('Ethernet Private Tree')), (TYPE_EVPTREE, _('Ethernet Virtual Private Tree')), )), + ('Other', ( + (TYPE_SPB, _('SPB')), + )), ) P2P = ( @@ -263,3 +267,17 @@ class L2VPNTypeChoices(ChoiceSet): TYPE_EPLAN, TYPE_EPTREE ) + + +class L2VPNStatusChoices(ChoiceSet): + key = 'L2VPN.status' + + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_DECOMMISSIONING = 'decommissioning' + + CHOICES = [ + (STATUS_ACTIVE, _('Active'), 'green'), + (STATUS_PLANNED, _('Planned'), 'cyan'), + (STATUS_DECOMMISSIONING, _('Decommissioning'), 'red'), + ] diff --git a/netbox/vpn/filtersets.py b/netbox/vpn/filtersets.py index 5b8d1899b..d35831e2f 100644 --- a/netbox/vpn/filtersets.py +++ b/netbox/vpn/filtersets.py @@ -298,6 +298,9 @@ class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterS choices=L2VPNTypeChoices, null_value=None ) + status = django_filters.MultipleChoiceFilter( + choices=L2VPNStatusChoices, + ) import_target_id = django_filters.ModelMultipleChoiceFilter( field_name='import_targets', queryset=RouteTarget.objects.all(), @@ -323,7 +326,7 @@ class L2VPNFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterS class Meta: model = L2VPN - fields = ('id', 'identifier', 'name', 'slug', 'type', 'description') + fields = ('id', 'identifier', 'name', 'slug', 'status', 'type', 'description') def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/vpn/forms/bulk_edit.py b/netbox/vpn/forms/bulk_edit.py index a7595a2a7..700dadb70 100644 --- a/netbox/vpn/forms/bulk_edit.py +++ b/netbox/vpn/forms/bulk_edit.py @@ -260,6 +260,10 @@ class IPSecProfileBulkEditForm(NetBoxModelBulkEditForm): class L2VPNBulkEditForm(NetBoxModelBulkEditForm): + status = forms.ChoiceField( + label=_('Status'), + choices=L2VPNStatusChoices, + ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(L2VPNTypeChoices), @@ -279,7 +283,7 @@ class L2VPNBulkEditForm(NetBoxModelBulkEditForm): model = L2VPN fieldsets = ( - FieldSet('type', 'tenant', 'description'), + FieldSet('status', 'type', 'tenant', 'description'), ) nullable_fields = ('tenant', 'description', 'comments') diff --git a/netbox/vpn/forms/bulk_import.py b/netbox/vpn/forms/bulk_import.py index b8d19bb38..925558e60 100644 --- a/netbox/vpn/forms/bulk_import.py +++ b/netbox/vpn/forms/bulk_import.py @@ -260,6 +260,11 @@ class L2VPNImportForm(NetBoxModelImportForm): required=False, to_field_name='name', ) + status = CSVChoiceField( + label=_('Status'), + choices=L2VPNStatusChoices, + help_text=_('Operational status') + ) type = CSVChoiceField( label=_('Type'), choices=L2VPNTypeChoices, diff --git a/netbox/vpn/forms/filtersets.py b/netbox/vpn/forms/filtersets.py index 619956156..4f814f709 100644 --- a/netbox/vpn/forms/filtersets.py +++ b/netbox/vpn/forms/filtersets.py @@ -215,10 +215,15 @@ class L2VPNFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFilt model = L2VPN fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('type', 'import_target_id', 'export_target_id', name=_('Attributes')), + FieldSet('type', 'status', 'import_target_id', 'export_target_id', name=_('Attributes')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), ) + status = forms.MultipleChoiceField( + label=_('Status'), + choices=L2VPNStatusChoices, + required=False + ) type = forms.ChoiceField( label=_('Type'), choices=add_blank_choice(L2VPNTypeChoices), diff --git a/netbox/vpn/forms/model_forms.py b/netbox/vpn/forms/model_forms.py index d6d02b4f5..1bf5b580c 100644 --- a/netbox/vpn/forms/model_forms.py +++ b/netbox/vpn/forms/model_forms.py @@ -409,7 +409,7 @@ class L2VPNForm(TenancyForm, NetBoxModelForm): comments = CommentField() fieldsets = ( - FieldSet('name', 'slug', 'type', 'identifier', 'description', 'tags', name=_('L2VPN')), + FieldSet('name', 'slug', 'type', 'status', 'identifier', 'description', 'tags', name=_('L2VPN')), FieldSet('import_targets', 'export_targets', name=_('Route Targets')), FieldSet('tenant_group', 'tenant', name=_('Tenancy')), ) @@ -417,8 +417,8 @@ class L2VPNForm(TenancyForm, NetBoxModelForm): class Meta: model = L2VPN fields = ( - 'name', 'slug', 'type', 'identifier', 'import_targets', 'export_targets', 'tenant', 'description', - 'comments', 'tags' + 'name', 'slug', 'type', 'status', 'identifier', 'import_targets', 'export_targets', 'tenant', + 'description', 'comments', 'tags' ) diff --git a/netbox/vpn/graphql/enums.py b/netbox/vpn/graphql/enums.py new file mode 100644 index 000000000..518528b83 --- /dev/null +++ b/netbox/vpn/graphql/enums.py @@ -0,0 +1,31 @@ +import strawberry + +from vpn.choices import * + +__all__ = ( + 'AuthenticationAlgorithmEnum', + 'AuthenticationMethodEnum', + 'DHGroupEnum', + 'EncryptionAlgorithmEnum', + 'IKEModeEnum', + 'IKEVersionEnum', + 'IPSecModeEnum', + 'L2VPNTypeEnum', + 'TunnelEncapsulationEnum', + 'TunnelStatusEnum', + 'TunnelTerminationRoleEnum', + 'TunnelTerminationTypeEnum', +) + +AuthenticationAlgorithmEnum = strawberry.enum(AuthenticationAlgorithmChoices.as_enum(prefix='auth')) +AuthenticationMethodEnum = strawberry.enum(AuthenticationMethodChoices.as_enum()) +DHGroupEnum = strawberry.enum(DHGroupChoices.as_enum(prefix='group')) +EncryptionAlgorithmEnum = strawberry.enum(EncryptionAlgorithmChoices.as_enum(prefix='encryption')) +IKEModeEnum = strawberry.enum(IKEModeChoices.as_enum()) +IKEVersionEnum = strawberry.enum(IKEVersionChoices.as_enum(prefix='version')) +IPSecModeEnum = strawberry.enum(IPSecModeChoices.as_enum()) +L2VPNTypeEnum = strawberry.enum(L2VPNTypeChoices.as_enum(prefix='type')) +TunnelEncapsulationEnum = strawberry.enum(TunnelEncapsulationChoices.as_enum(prefix='encap')) +TunnelStatusEnum = strawberry.enum(TunnelStatusChoices.as_enum(prefix='status')) +TunnelTerminationRoleEnum = strawberry.enum(TunnelTerminationRoleChoices.as_enum(prefix='role')) +TunnelTerminationTypeEnum = strawberry.enum(TunnelTerminationTypeChoices.as_enum(prefix='type')) diff --git a/netbox/vpn/graphql/filters.py b/netbox/vpn/graphql/filters.py index 34594458b..f3ee290fe 100644 --- a/netbox/vpn/graphql/filters.py +++ b/netbox/vpn/graphql/filters.py @@ -1,7 +1,21 @@ -import strawberry_django +from typing import Annotated, TYPE_CHECKING -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from vpn import filtersets, models +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, OrganizationalModelFilterMixin, PrimaryModelFilterMixin +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin +from vpn import models + +if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from ipam.graphql.filters import IPAddressFilter, RouteTargetFilter + from netbox.graphql.filter_lookups import IntegerLookup + from .enums import * __all__ = ( 'TunnelGroupFilter', @@ -18,60 +32,155 @@ __all__ = ( @strawberry_django.filter(models.TunnelGroup, lookups=True) -@autotype_decorator(filtersets.TunnelGroupFilterSet) -class TunnelGroupFilter(BaseFilterMixin): +class TunnelGroupFilter(OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.TunnelTermination, lookups=True) -@autotype_decorator(filtersets.TunnelTerminationFilterSet) -class TunnelTerminationFilter(BaseFilterMixin): - pass +class TunnelTerminationFilter( + BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin +): + tunnel: Annotated['TunnelFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + tunnel_id: ID | None = strawberry_django.filter_field() + role: Annotated['TunnelTerminationRoleEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type: Annotated['TunnelTerminationTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type_id: ID | None = strawberry_django.filter_field() + termination_id: ID | None = strawberry_django.filter_field() + outside_ip: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + outside_ip_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Tunnel, lookups=True) -@autotype_decorator(filtersets.TunnelFilterSet) -class TunnelFilter(BaseFilterMixin): - pass +class TunnelFilter(TenancyFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['TunnelGroupFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + encapsulation: Annotated['TunnelEncapsulationEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + ipsec_profile: Annotated['IPSecProfileFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tunnel_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + terminations: Annotated['TunnelTerminationFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IKEProposal, lookups=True) -@autotype_decorator(filtersets.IKEProposalFilterSet) -class IKEProposalFilter(BaseFilterMixin): - pass +class IKEProposalFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + authentication_method: Annotated['AuthenticationMethodEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + encryption_algorithm: Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + sa_lifetime: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + ike_policies: Annotated['IKEPolicyFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IKEPolicy, lookups=True) -@autotype_decorator(filtersets.IKEPolicyFilterSet) -class IKEPolicyFilter(BaseFilterMixin): - pass +class IKEPolicyFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + version: Annotated['IKEVersionEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + mode: Annotated['IKEModeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + proposals: Annotated['IKEProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + preshared_key: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.IPSecProposal, lookups=True) -@autotype_decorator(filtersets.IPSecProposalFilterSet) -class IPSecProposalFilter(BaseFilterMixin): - pass +class IPSecProposalFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + encryption_algorithm: Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + sa_lifetime_seconds: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + sa_lifetime_data: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + ipsec_policies: Annotated['IPSecPolicyFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IPSecPolicy, lookups=True) -@autotype_decorator(filtersets.IPSecPolicyFilterSet) -class IPSecPolicyFilter(BaseFilterMixin): - pass +class IPSecPolicyFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + proposals: Annotated['IPSecProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + pfs_group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.IPSecProfile, lookups=True) -@autotype_decorator(filtersets.IPSecProfileFilterSet) -class IPSecProfileFilter(BaseFilterMixin): - pass +class IPSecProfileFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + mode: Annotated['IPSecModeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + ike_policy: Annotated['IKEPolicyFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ike_policy_id: ID | None = strawberry_django.filter_field() + ipsec_policy: Annotated['IPSecPolicyFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ipsec_policy_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.L2VPN, lookups=True) -@autotype_decorator(filtersets.L2VPNFilterSet) -class L2VPNFilter(BaseFilterMixin): - pass +class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + type: Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + identifier: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + export_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + terminations: Annotated['L2VPNTerminationFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.L2VPNTermination, lookups=True) -@autotype_decorator(filtersets.L2VPNTerminationFilterSet) -class L2VPNTerminationFilter(BaseFilterMixin): - pass +class L2VPNTerminationFilter(NetBoxModelFilterMixin): + l2vpn: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + l2vpn_id: ID | None = strawberry_django.filter_field() + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/vpn/graphql/types.py b/netbox/vpn/graphql/types.py index 3fec6e791..eeb050819 100644 --- a/netbox/vpn/graphql/types.py +++ b/netbox/vpn/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -8,6 +8,13 @@ from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObj from vpn import models from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import InterfaceType + from ipam.graphql.types import IPAddressType, RouteTargetType, VLANType + from netbox.graphql.types import ContentTypeType + from tenancy.graphql.types import TenantType + from virtualization.graphql.types import VMInterfaceType + __all__ = ( 'IKEPolicyType', 'IKEProposalType', @@ -25,7 +32,8 @@ __all__ = ( @strawberry_django.type( models.TunnelGroup, fields='__all__', - filters=TunnelGroupFilter + filters=TunnelGroupFilter, + pagination=True ) class TunnelGroupType(ContactsMixin, OrganizationalObjectType): @@ -35,7 +43,8 @@ class TunnelGroupType(ContactsMixin, OrganizationalObjectType): @strawberry_django.type( models.TunnelTermination, fields='__all__', - filters=TunnelTerminationFilter + filters=TunnelTerminationFilter, + pagination=True ) class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): tunnel: Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')] @@ -46,7 +55,8 @@ class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): @strawberry_django.type( models.Tunnel, fields='__all__', - filters=TunnelFilter + filters=TunnelFilter, + pagination=True ) class TunnelType(ContactsMixin, NetBoxObjectType): group: Annotated["TunnelGroupType", strawberry.lazy('vpn.graphql.types')] | None @@ -59,7 +69,8 @@ class TunnelType(ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.IKEProposal, fields='__all__', - filters=IKEProposalFilter + filters=IKEProposalFilter, + pagination=True ) class IKEProposalType(OrganizationalObjectType): @@ -69,7 +80,8 @@ class IKEProposalType(OrganizationalObjectType): @strawberry_django.type( models.IKEPolicy, fields='__all__', - filters=IKEPolicyFilter + filters=IKEPolicyFilter, + pagination=True ) class IKEPolicyType(OrganizationalObjectType): @@ -80,7 +92,8 @@ class IKEPolicyType(OrganizationalObjectType): @strawberry_django.type( models.IPSecProposal, fields='__all__', - filters=IPSecProposalFilter + filters=IPSecProposalFilter, + pagination=True ) class IPSecProposalType(OrganizationalObjectType): @@ -90,7 +103,8 @@ class IPSecProposalType(OrganizationalObjectType): @strawberry_django.type( models.IPSecPolicy, fields='__all__', - filters=IPSecPolicyFilter + filters=IPSecPolicyFilter, + pagination=True ) class IPSecPolicyType(OrganizationalObjectType): @@ -101,7 +115,8 @@ class IPSecPolicyType(OrganizationalObjectType): @strawberry_django.type( models.IPSecProfile, fields='__all__', - filters=IPSecProfileFilter + filters=IPSecProfileFilter, + pagination=True ) class IPSecProfileType(OrganizationalObjectType): ike_policy: Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')] @@ -113,7 +128,8 @@ class IPSecProfileType(OrganizationalObjectType): @strawberry_django.type( models.L2VPN, fields='__all__', - filters=L2VPNFilter + filters=L2VPNFilter, + pagination=True ) class L2VPNType(ContactsMixin, NetBoxObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -125,8 +141,9 @@ class L2VPNType(ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.L2VPNTermination, - exclude=('assigned_object_type', 'assigned_object_id'), - filters=L2VPNTerminationFilter + exclude=['assigned_object_type', 'assigned_object_id'], + filters=L2VPNTerminationFilter, + pagination=True ) class L2VPNTerminationType(NetBoxObjectType): l2vpn: Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')] diff --git a/netbox/vpn/migrations/0001_initial.py b/netbox/vpn/migrations/0001_initial.py index b44ae3e52..eed3c6329 100644 --- a/netbox/vpn/migrations/0001_initial.py +++ b/netbox/vpn/migrations/0001_initial.py @@ -10,7 +10,7 @@ class Migration(migrations.Migration): dependencies = [ ('contenttypes', '0002_remove_content_type_name'), ('extras', '0099_cachedvalue_ordering'), - ('ipam', '0067_ipaddress_index_host'), + ('ipam', '0054_squashed_0067'), ('tenancy', '0012_contactassignment_custom_fields'), ] diff --git a/netbox/vpn/migrations/0002_move_l2vpn.py b/netbox/vpn/migrations/0002_move_l2vpn.py index 5f1480dce..41ccb8a8d 100644 --- a/netbox/vpn/migrations/0002_move_l2vpn.py +++ b/netbox/vpn/migrations/0002_move_l2vpn.py @@ -72,14 +72,6 @@ class Migration(migrations.Migration): ( 'assigned_object_type', models.ForeignKey( - limit_choices_to=models.Q( - models.Q( - models.Q(('app_label', 'dcim'), ('model', 'interface')), - models.Q(('app_label', 'ipam'), ('model', 'vlan')), - models.Q(('app_label', 'virtualization'), ('model', 'vminterface')), - _connector='OR', - ) - ), on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.contenttype', diff --git a/netbox/vpn/migrations/0008_add_l2vpn_status.py b/netbox/vpn/migrations/0008_add_l2vpn_status.py new file mode 100644 index 000000000..8b0267e45 --- /dev/null +++ b/netbox/vpn/migrations/0008_add_l2vpn_status.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('vpn', '0007_natural_ordering'), + ] + + operations = [ + migrations.AddField( + model_name='l2vpn', + name='status', + field=models.CharField(default='active', max_length=50), + ), + ] diff --git a/netbox/vpn/migrations/0009_remove_redundant_indexes.py b/netbox/vpn/migrations/0009_remove_redundant_indexes.py new file mode 100644 index 000000000..9f474f9ed --- /dev/null +++ b/netbox/vpn/migrations/0009_remove_redundant_indexes.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2b1 on 2025-04-03 18:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('vpn', '0008_add_l2vpn_status'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='l2vpntermination', + name='vpn_l2vpnte_assigne_9c55f8_idx', + ), + migrations.RemoveIndex( + model_name='tunneltermination', + name='vpn_tunnelt_termina_c1f04b_idx', + ), + ] diff --git a/netbox/vpn/models/l2vpn.py b/netbox/vpn/models/l2vpn.py index 3e562531d..bab7510ff 100644 --- a/netbox/vpn/models/l2vpn.py +++ b/netbox/vpn/models/l2vpn.py @@ -7,8 +7,7 @@ from django.utils.translation import gettext_lazy as _ from core.models import ObjectType from netbox.models import NetBoxModel, PrimaryModel from netbox.models.features import ContactsMixin -from vpn.choices import L2VPNTypeChoices -from vpn.constants import L2VPN_ASSIGNMENT_MODELS +from vpn.choices import L2VPNStatusChoices, L2VPNTypeChoices __all__ = ( 'L2VPN', @@ -33,6 +32,12 @@ class L2VPN(ContactsMixin, PrimaryModel): max_length=50, choices=L2VPNTypeChoices ) + status = models.CharField( + verbose_name=_('status'), + max_length=50, + choices=L2VPNStatusChoices, + default=L2VPNStatusChoices.STATUS_ACTIVE, + ) identifier = models.BigIntegerField( verbose_name=_('identifier'), null=True, @@ -56,7 +61,7 @@ class L2VPN(ContactsMixin, PrimaryModel): null=True ) - clone_fields = ('type',) + clone_fields = ('type', 'status') class Meta: ordering = ('name', 'identifier') @@ -68,6 +73,9 @@ class L2VPN(ContactsMixin, PrimaryModel): return f'{self.name} ({self.identifier})' return f'{self.name}' + def get_status_color(self): + return L2VPNStatusChoices.colors.get(self.status) + @cached_property def can_add_termination(self): if self.type in L2VPNTypeChoices.P2P and self.terminations.count() >= 2: @@ -84,7 +92,6 @@ class L2VPNTermination(NetBoxModel): ) assigned_object_type = models.ForeignKey( to='contenttypes.ContentType', - limit_choices_to=L2VPN_ASSIGNMENT_MODELS, on_delete=models.PROTECT, related_name='+' ) @@ -101,9 +108,6 @@ class L2VPNTermination(NetBoxModel): class Meta: ordering = ('l2vpn',) - indexes = ( - models.Index(fields=('assigned_object_type', 'assigned_object_id')), - ) constraints = ( models.UniqueConstraint( fields=('assigned_object_type', 'assigned_object_id'), diff --git a/netbox/vpn/models/tunnels.py b/netbox/vpn/models/tunnels.py index 9372bd535..a25036789 100644 --- a/netbox/vpn/models/tunnels.py +++ b/netbox/vpn/models/tunnels.py @@ -138,9 +138,6 @@ class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLo class Meta: ordering = ('tunnel', 'role', 'pk') - indexes = ( - models.Index(fields=('termination_type', 'termination_id')), - ) constraints = ( models.UniqueConstraint( fields=('termination_type', 'termination_id'), diff --git a/netbox/vpn/search.py b/netbox/vpn/search.py index c1914dc22..07ab9a5ca 100644 --- a/netbox/vpn/search.py +++ b/netbox/vpn/search.py @@ -79,4 +79,4 @@ class L2VPNIndex(SearchIndex): ('description', 500), ('comments', 5000), ) - display_attrs = ('type', 'identifier', 'tenant', 'description') + display_attrs = ('type', 'status', 'identifier', 'tenant', 'description') diff --git a/netbox/vpn/tables/l2vpn.py b/netbox/vpn/tables/l2vpn.py index 9a614ab98..95586461e 100644 --- a/netbox/vpn/tables/l2vpn.py +++ b/netbox/vpn/tables/l2vpn.py @@ -23,6 +23,9 @@ class L2VPNTable(TenancyColumnsMixin, NetBoxTable): verbose_name=_('Name'), linkify=True ) + status = columns.ChoiceFieldColumn( + verbose_name=_('Status') + ) import_targets = columns.TemplateColumn( verbose_name=_('Import Targets'), template_code=L2VPN_TARGETS, @@ -43,10 +46,10 @@ class L2VPNTable(TenancyColumnsMixin, NetBoxTable): class Meta(NetBoxTable.Meta): model = L2VPN fields = ( - 'pk', 'name', 'slug', 'identifier', 'type', 'import_targets', 'export_targets', 'tenant', 'tenant_group', - 'description', 'comments', 'tags', 'created', 'last_updated', + 'pk', 'name', 'slug', 'status', 'identifier', 'type', 'import_targets', 'export_targets', 'tenant', + 'tenant_group', 'description', 'comments', 'tags', 'created', 'last_updated', ) - default_columns = ('pk', 'name', 'identifier', 'type', 'description') + default_columns = ('pk', 'name', 'status', 'identifier', 'type', 'description') class L2VPNTerminationTable(NetBoxTable): diff --git a/netbox/vpn/tests/test_api.py b/netbox/vpn/tests/test_api.py index f2d43718f..19fdf1136 100644 --- a/netbox/vpn/tests/test_api.py +++ b/netbox/vpn/tests/test_api.py @@ -1,4 +1,5 @@ from django.urls import reverse +from rest_framework import status from dcim.choices import InterfaceTypeChoices from dcim.models import Interface @@ -527,19 +528,22 @@ class L2VPNTest(APIViewTestCases.APIViewTestCase): 'name': 'L2VPN 4', 'slug': 'l2vpn-4', 'type': 'vxlan', - 'identifier': 33343344 + 'identifier': 33343344, + 'status': L2VPNStatusChoices.STATUS_ACTIVE, }, { 'name': 'L2VPN 5', 'slug': 'l2vpn-5', 'type': 'vxlan', - 'identifier': 33343345 + 'identifier': 33343345, + 'status': L2VPNStatusChoices.STATUS_PLANNED, }, { 'name': 'L2VPN 6', 'slug': 'l2vpn-6', 'type': 'vpws', - 'identifier': 33343346 + 'identifier': 33343346, + 'status': L2VPNStatusChoices.STATUS_DECOMMISSIONING, }, ] bulk_update_data = { @@ -550,12 +554,53 @@ class L2VPNTest(APIViewTestCases.APIViewTestCase): def setUpTestData(cls): l2vpns = ( - L2VPN(name='L2VPN 1', slug='l2vpn-1', type='vxlan', identifier=650001), - L2VPN(name='L2VPN 2', slug='l2vpn-2', type='vpws', identifier=650002), - L2VPN(name='L2VPN 3', slug='l2vpn-3', type='vpls'), # No RD + L2VPN( + name='L2VPN 1', slug='l2vpn-1', type='vxlan', identifier=650001, + status=L2VPNStatusChoices.STATUS_ACTIVE, + ), + L2VPN( + name='L2VPN 2', slug='l2vpn-2', type='vpws', identifier=650002, + status=L2VPNStatusChoices.STATUS_PLANNED, + ), + L2VPN( + name='L2VPN 3', slug='l2vpn-3', type='vpls', + status=L2VPNStatusChoices.STATUS_DECOMMISSIONING, + ), # No RD ) L2VPN.objects.bulk_create(l2vpns) + def test_status_filter(self): + url = reverse('vpn-api:l2vpn-list') + + self.add_permissions('vpn.view_l2vpn') + response = self.client.get(url, **self.header) + response_data = response.json() + + # all L2VPNs present with not filter + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response_data['count'], 3) + + # 1 L2VPN present with active status filter + filter_url = f'{url}?status={L2VPNStatusChoices.STATUS_ACTIVE}' + response = self.client.get(filter_url, **self.header) + response_data = response.json() + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response_data['count'], 1) + + # 2 L2VPNs present with active and planned status filter + filter_url = f'{filter_url}&status={L2VPNStatusChoices.STATUS_PLANNED}' + response = self.client.get(filter_url, **self.header) + response_data = response.json() + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response_data['count'], 2) + + # 1 L2VPN present with decommissioning status filter + filter_url = f'{url}?status={L2VPNStatusChoices.STATUS_DECOMMISSIONING}' + response = self.client.get(filter_url, **self.header) + response_data = response.json() + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response_data['count'], 1) + class L2VPNTerminationTest(APIViewTestCases.APIViewTestCase): model = L2VPNTermination diff --git a/netbox/vpn/tests/test_filtersets.py b/netbox/vpn/tests/test_filtersets.py index d2b893766..ee1f9ca72 100644 --- a/netbox/vpn/tests/test_filtersets.py +++ b/netbox/vpn/tests/test_filtersets.py @@ -769,6 +769,7 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests): name='L2VPN 1', slug='l2vpn-1', type=L2VPNTypeChoices.TYPE_VXLAN, + status=L2VPNStatusChoices.STATUS_ACTIVE, identifier=65001, description='foobar1' ), @@ -776,6 +777,7 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests): name='L2VPN 2', slug='l2vpn-2', type=L2VPNTypeChoices.TYPE_VPWS, + status=L2VPNStatusChoices.STATUS_PLANNED, identifier=65002, description='foobar2' ), @@ -783,6 +785,7 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests): name='L2VPN 3', slug='l2vpn-3', type=L2VPNTypeChoices.TYPE_VPLS, + status=L2VPNStatusChoices.STATUS_DECOMMISSIONING, description='foobar3' ), ) @@ -814,6 +817,15 @@ class L2VPNTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'type': [L2VPNTypeChoices.TYPE_VXLAN, L2VPNTypeChoices.TYPE_VPWS]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_status(self): + self.assertEqual(self.filterset({}, self.queryset).qs.count(), 3) + + params = {'status': [L2VPNStatusChoices.STATUS_ACTIVE, L2VPNStatusChoices.STATUS_PLANNED]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + + params = {'status': [L2VPNStatusChoices.STATUS_DECOMMISSIONING]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_description(self): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) diff --git a/netbox/vpn/tests/test_views.py b/netbox/vpn/tests/test_views.py index 05ac527fe..6d2239169 100644 --- a/netbox/vpn/tests/test_views.py +++ b/netbox/vpn/tests/test_views.py @@ -574,16 +574,25 @@ class L2VPNTestCase(ViewTestCases.PrimaryObjectViewTestCase): RouteTarget.objects.bulk_create(rts) l2vpns = ( - L2VPN(name='L2VPN 1', slug='l2vpn-1', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650001'), - L2VPN(name='L2VPN 2', slug='l2vpn-2', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650002'), - L2VPN(name='L2VPN 3', slug='l2vpn-3', type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650003') + L2VPN( + name='L2VPN 1', slug='l2vpn-1', status=L2VPNStatusChoices.STATUS_ACTIVE, + type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650001' + ), + L2VPN( + name='L2VPN 2', slug='l2vpn-2', status=L2VPNStatusChoices.STATUS_DECOMMISSIONING, + type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650002' + ), + L2VPN( + name='L2VPN 3', slug='l2vpn-3', status=L2VPNStatusChoices.STATUS_PLANNED, + type=L2VPNTypeChoices.TYPE_VXLAN, identifier='650003' + ) ) L2VPN.objects.bulk_create(l2vpns) cls.csv_data = ( - 'name,slug,type,identifier', - 'L2VPN 5,l2vpn-5,vxlan,456', - 'L2VPN 6,l2vpn-6,vxlan,444', + 'name,status,slug,type,identifier', + 'L2VPN 5,active,l2vpn-5,vxlan,456', + 'L2VPN 6,planned,l2vpn-6,vxlan,444', ) cls.csv_update_data = ( @@ -594,12 +603,14 @@ class L2VPNTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.bulk_edit_data = { 'description': 'New Description', + 'status': L2VPNStatusChoices.STATUS_DECOMMISSIONING, } cls.form_data = { 'name': 'L2VPN 8', 'slug': 'l2vpn-8', 'type': L2VPNTypeChoices.TYPE_VXLAN, + 'status': L2VPNStatusChoices.STATUS_PLANNED, 'identifier': 123, 'description': 'Description', 'import_targets': [rts[0].pk], diff --git a/netbox/vpn/views.py b/netbox/vpn/views.py index 83d5ad2c4..a729005a2 100644 --- a/netbox/vpn/views.py +++ b/netbox/vpn/views.py @@ -1,6 +1,5 @@ from ipam.tables import RouteTargetTable from netbox.views import generic -from tenancy.views import ObjectContactsView from utilities.query import count_related from utilities.views import GetRelatedModelsMixin, register_model_view from . import filtersets, forms, tables @@ -68,11 +67,6 @@ class TunnelGroupBulkDeleteView(generic.BulkDeleteView): table = tables.TunnelGroupTable -@register_model_view(TunnelGroup, 'contacts') -class TunnelGroupContactsView(ObjectContactsView): - queryset = TunnelGroup.objects.all() - - # # Tunnels # @@ -137,11 +131,6 @@ class TunnelBulkDeleteView(generic.BulkDeleteView): table = tables.TunnelTable -@register_model_view(Tunnel, 'contacts') -class TunnelContactsView(ObjectContactsView): - queryset = Tunnel.objects.all() - - # # Tunnel terminations # @@ -507,11 +496,6 @@ class L2VPNBulkDeleteView(generic.BulkDeleteView): table = tables.L2VPNTable -@register_model_view(L2VPN, 'contacts') -class L2VPNContactsView(ObjectContactsView): - queryset = L2VPN.objects.all() - - # # L2VPN terminations # diff --git a/netbox/wireless/api/serializers_/wirelesslans.py b/netbox/wireless/api/serializers_/wirelesslans.py index 68f79daf6..97d57f9f5 100644 --- a/netbox/wireless/api/serializers_/wirelesslans.py +++ b/netbox/wireless/api/serializers_/wirelesslans.py @@ -26,7 +26,7 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer): model = WirelessLANGroup fields = [ 'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields', - 'created', 'last_updated', 'wirelesslan_count', '_depth', + 'created', 'last_updated', 'wirelesslan_count', 'comments', '_depth', ] brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'wirelesslan_count', '_depth') diff --git a/netbox/wireless/filtersets.py b/netbox/wireless/filtersets.py index cc5aefbd8..bd96865ad 100644 --- a/netbox/wireless/filtersets.py +++ b/netbox/wireless/filtersets.py @@ -5,7 +5,7 @@ from dcim.choices import LinkStatusChoices from dcim.base_filtersets import ScopedFilterSet from dcim.models import Interface from ipam.models import VLAN -from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet +from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet from tenancy.filtersets import TenancyFilterSet from utilities.filters import TreeNodeMultipleChoiceFilter from .choices import * @@ -18,7 +18,7 @@ __all__ = ( ) -class WirelessLANGroupFilterSet(OrganizationalModelFilterSet): +class WirelessLANGroupFilterSet(NestedGroupModelFilterSet): parent_id = django_filters.ModelMultipleChoiceFilter( queryset=WirelessLANGroup.objects.all() ) diff --git a/netbox/wireless/forms/bulk_edit.py b/netbox/wireless/forms/bulk_edit.py index 5cd3a157a..1a75512e1 100644 --- a/netbox/wireless/forms/bulk_edit.py +++ b/netbox/wireless/forms/bulk_edit.py @@ -32,12 +32,13 @@ class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + comments = CommentField() model = WirelessLANGroup fieldsets = ( FieldSet('parent', 'description'), ) - nullable_fields = ('parent', 'description') + nullable_fields = ('parent', 'description', 'comments') class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm): diff --git a/netbox/wireless/forms/bulk_import.py b/netbox/wireless/forms/bulk_import.py index 1fece7e46..389dcf25d 100644 --- a/netbox/wireless/forms/bulk_import.py +++ b/netbox/wireless/forms/bulk_import.py @@ -30,7 +30,7 @@ class WirelessLANGroupImportForm(NetBoxModelImportForm): class Meta: model = WirelessLANGroup - fields = ('name', 'slug', 'parent', 'description', 'tags') + fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments') class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm): diff --git a/netbox/wireless/forms/model_forms.py b/netbox/wireless/forms/model_forms.py index 9cfcca7ba..56422ab57 100644 --- a/netbox/wireless/forms/model_forms.py +++ b/netbox/wireless/forms/model_forms.py @@ -24,6 +24,7 @@ class WirelessLANGroupForm(NetBoxModelForm): required=False ) slug = SlugField() + comments = CommentField() fieldsets = ( FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Wireless LAN Group')), @@ -32,7 +33,7 @@ class WirelessLANGroupForm(NetBoxModelForm): class Meta: model = WirelessLANGroup fields = [ - 'parent', 'name', 'slug', 'description', 'tags', + 'parent', 'name', 'slug', 'description', 'tags', 'comments', ] diff --git a/netbox/wireless/graphql/enums.py b/netbox/wireless/graphql/enums.py new file mode 100644 index 000000000..7b96446de --- /dev/null +++ b/netbox/wireless/graphql/enums.py @@ -0,0 +1,17 @@ +import strawberry + +from wireless.choices import * + +__all__ = ( + 'WirelessAuthCipherEnum', + 'WirelessAuthTypeEnum', + 'WirelessChannelEnum', + 'WirelessLANStatusEnum', + 'WirelessRoleEnum', +) + +WirelessAuthCipherEnum = strawberry.enum(WirelessAuthCipherChoices.as_enum(prefix='cipher')) +WirelessAuthTypeEnum = strawberry.enum(WirelessAuthTypeChoices.as_enum(prefix='type')) +WirelessChannelEnum = strawberry.enum(WirelessChannelChoices.as_enum(prefix='channel')) +WirelessLANStatusEnum = strawberry.enum(WirelessLANStatusChoices.as_enum(prefix='status')) +WirelessRoleEnum = strawberry.enum(WirelessRoleChoices.as_enum(prefix='role')) diff --git a/netbox/wireless/graphql/filter_mixins.py b/netbox/wireless/graphql/filter_mixins.py new file mode 100644 index 000000000..636bc8a52 --- /dev/null +++ b/netbox/wireless/graphql/filter_mixins.py @@ -0,0 +1,26 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING + +import strawberry +import strawberry_django +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from .enums import * + +__all__ = ( + 'WirelessAuthenticationBaseFilterMixin', +) + + +@dataclass +class WirelessAuthenticationBaseFilterMixin(BaseFilterMixin): + auth_type: Annotated['WirelessAuthTypeEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_cipher: Annotated['WirelessAuthCipherEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_psk: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index 47d04bedc..d71af7ae2 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -1,7 +1,20 @@ -import strawberry_django +from typing import Annotated, TYPE_CHECKING -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from wireless import filtersets, models +import strawberry +import strawberry_django +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from dcim.graphql.filter_mixins import ScopedFilterMixin +from netbox.graphql.filter_mixins import DistanceFilterMixin, PrimaryModelFilterMixin, NestedGroupModelFilterMixin +from tenancy.graphql.filter_mixins import TenancyFilterMixin +from wireless import models +from .filter_mixins import WirelessAuthenticationBaseFilterMixin + +if TYPE_CHECKING: + from dcim.graphql.filters import InterfaceFilter + from ipam.graphql.filters import VLANFilter + from .enums import * __all__ = ( 'WirelessLANGroupFilter', @@ -11,18 +24,45 @@ __all__ = ( @strawberry_django.filter(models.WirelessLANGroup, lookups=True) -@autotype_decorator(filtersets.WirelessLANGroupFilterSet) -class WirelessLANGroupFilter(BaseFilterMixin): +class WirelessLANGroupFilter(NestedGroupModelFilterMixin): pass @strawberry_django.filter(models.WirelessLAN, lookups=True) -@autotype_decorator(filtersets.WirelessLANFilterSet) -class WirelessLANFilter(BaseFilterMixin): - pass +class WirelessLANFilter( + WirelessAuthenticationBaseFilterMixin, + ScopedFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin +): + ssid: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['WirelessLANGroupFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + vlan_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.WirelessLink, lookups=True) -@autotype_decorator(filtersets.WirelessLinkFilterSet) -class WirelessLinkFilter(BaseFilterMixin): - pass +class WirelessLinkFilter( + WirelessAuthenticationBaseFilterMixin, + DistanceFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin +): + interface_a: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_a_id: ID | None = strawberry_django.filter_field() + interface_b: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_b_id: ID | None = strawberry_django.filter_field() + ssid: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py index aa44e9b9f..eeca6a82b 100644 --- a/netbox/wireless/graphql/types.py +++ b/netbox/wireless/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -7,6 +7,11 @@ from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType from wireless import models from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import DeviceType, InterfaceType, LocationType, RegionType, SiteGroupType, SiteType + from ipam.graphql.types import VLANType + from tenancy.graphql.types import TenantType + __all__ = ( 'WirelessLANType', 'WirelessLANGroupType', @@ -17,7 +22,8 @@ __all__ = ( @strawberry_django.type( models.WirelessLANGroup, fields='__all__', - filters=WirelessLANGroupFilter + filters=WirelessLANGroupFilter, + pagination=True ) class WirelessLANGroupType(OrganizationalObjectType): parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None @@ -28,8 +34,9 @@ class WirelessLANGroupType(OrganizationalObjectType): @strawberry_django.type( models.WirelessLAN, - exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), - filters=WirelessLANFilter + exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], + filters=WirelessLANFilter, + pagination=True ) class WirelessLANType(NetBoxObjectType): group: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None @@ -51,7 +58,8 @@ class WirelessLANType(NetBoxObjectType): @strawberry_django.type( models.WirelessLink, fields='__all__', - filters=WirelessLinkFilter + filters=WirelessLinkFilter, + pagination=True ) class WirelessLinkType(NetBoxObjectType): interface_a: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] diff --git a/netbox/wireless/migrations/0001_squashed_0008.py b/netbox/wireless/migrations/0001_squashed_0008.py index 8886580e1..2ffc287f9 100644 --- a/netbox/wireless/migrations/0001_squashed_0008.py +++ b/netbox/wireless/migrations/0001_squashed_0008.py @@ -4,7 +4,6 @@ import taggit.managers from django.db import migrations, models import utilities.json -import wireless.models class Migration(migrations.Migration): @@ -21,7 +20,7 @@ class Migration(migrations.Migration): dependencies = [ ('ipam', '0002_squashed_0046'), - ('tenancy', '0007_contact_link'), + ('tenancy', '0002_squashed_0011'), ('extras', '0002_squashed_0059'), ('dcim', '0003_squashed_0130'), ] @@ -149,7 +148,6 @@ class Migration(migrations.Migration): ( 'interface_a', models.ForeignKey( - limit_choices_to=wireless.models.get_wireless_interface_types, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='dcim.interface', @@ -158,7 +156,6 @@ class Migration(migrations.Migration): ( 'interface_b', models.ForeignKey( - limit_choices_to=wireless.models.get_wireless_interface_types, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='dcim.interface', diff --git a/netbox/wireless/migrations/0011_wirelesslan__location_wirelesslan__region_and_more.py b/netbox/wireless/migrations/0011_wirelesslan__location_wirelesslan__region_and_more.py index 334d41bdd..bac1819dd 100644 --- a/netbox/wireless/migrations/0011_wirelesslan__location_wirelesslan__region_and_more.py +++ b/netbox/wireless/migrations/0011_wirelesslan__location_wirelesslan__region_and_more.py @@ -66,7 +66,6 @@ class Migration(migrations.Migration): name='scope_type', field=models.ForeignKey( blank=True, - limit_choices_to=models.Q(('model__in', ('region', 'sitegroup', 'site', 'location'))), null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', diff --git a/netbox/wireless/migrations/0014_wirelesslangroup_comments.py b/netbox/wireless/migrations/0014_wirelesslangroup_comments.py new file mode 100644 index 000000000..9fc1a99d6 --- /dev/null +++ b/netbox/wireless/migrations/0014_wirelesslangroup_comments.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wireless', '0013_natural_ordering'), + ] + + operations = [ + migrations.AddField( + model_name='wirelesslangroup', + name='comments', + field=models.TextField(blank=True), + ), + ] diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py index 61ff72bc1..6cdc6fc5b 100644 --- a/netbox/wireless/models.py +++ b/netbox/wireless/models.py @@ -123,26 +123,18 @@ class WirelessLAN(WirelessAuthenticationBase, CachedScopeMixin, PrimaryModel): return WirelessLANStatusChoices.colors.get(self.status) -def get_wireless_interface_types(): - # Wrap choices in a callable to avoid generating dummy migrations - # when the choices are updated. - return {'type__in': WIRELESS_IFACE_TYPES} - - class WirelessLink(WirelessAuthenticationBase, DistanceMixin, PrimaryModel): """ A point-to-point connection between two wireless Interfaces. """ interface_a = models.ForeignKey( to='dcim.Interface', - limit_choices_to=get_wireless_interface_types, on_delete=models.PROTECT, related_name='+', verbose_name=_('interface A'), ) interface_b = models.ForeignKey( to='dcim.Interface', - limit_choices_to=get_wireless_interface_types, on_delete=models.PROTECT, related_name='+', verbose_name=_('interface B'), diff --git a/netbox/wireless/search.py b/netbox/wireless/search.py index e1be53c09..3c1565cb7 100644 --- a/netbox/wireless/search.py +++ b/netbox/wireless/search.py @@ -21,6 +21,7 @@ class WirelessLANGroupIndex(SearchIndex): ('name', 100), ('slug', 110), ('description', 500), + ('comments', 5000), ) display_attrs = ('description',) diff --git a/netbox/wireless/tests/test_api.py b/netbox/wireless/tests/test_api.py index f768eafaf..0fe5e45f6 100644 --- a/netbox/wireless/tests/test_api.py +++ b/netbox/wireless/tests/test_api.py @@ -24,10 +24,12 @@ class WirelessLANGroupTest(APIViewTestCases.APIViewTestCase): { 'name': 'Wireless LAN Group 4', 'slug': 'wireless-lan-group-4', + 'comments': '', }, { 'name': 'Wireless LAN Group 5', 'slug': 'wireless-lan-group-5', + 'comments': 'LAN Group 5 comment', }, { 'name': 'Wireless LAN Group 6', @@ -36,6 +38,7 @@ class WirelessLANGroupTest(APIViewTestCases.APIViewTestCase): ] bulk_update_data = { 'description': 'New description', + 'comments': 'New comment', } @classmethod diff --git a/netbox/wireless/tests/test_filtersets.py b/netbox/wireless/tests/test_filtersets.py index 27aab83d8..9e8905d4a 100644 --- a/netbox/wireless/tests/test_filtersets.py +++ b/netbox/wireless/tests/test_filtersets.py @@ -21,7 +21,10 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): parent_groups = ( WirelessLANGroup(name='Wireless LAN Group 1', slug='wireless-lan-group-1', description='A'), WirelessLANGroup(name='Wireless LAN Group 2', slug='wireless-lan-group-2', description='B'), - WirelessLANGroup(name='Wireless LAN Group 3', slug='wireless-lan-group-3', description='C'), + WirelessLANGroup( + name='Wireless LAN Group 3', slug='wireless-lan-group-3', description='C', + comments='Parent Group 3 comment', + ), ) for group in parent_groups: group.save() @@ -38,10 +41,15 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): slug='wireless-lan-group-1b', parent=parent_groups[0], description='foobar2', + comments='Child Group 1B comment', ), WirelessLANGroup(name='Wireless LAN Group 2A', slug='wireless-lan-group-2a', parent=parent_groups[1]), WirelessLANGroup(name='Wireless LAN Group 2B', slug='wireless-lan-group-2b', parent=parent_groups[1]), - WirelessLANGroup(name='Wireless LAN Group 3A', slug='wireless-lan-group-3a', parent=parent_groups[2]), + WirelessLANGroup( + name='Wireless LAN Group 3A', slug='wireless-lan-group-3a', parent=parent_groups[2], + comments='Wireless LAN Group 3A comment', + + ), WirelessLANGroup(name='Wireless LAN Group 3B', slug='wireless-lan-group-3b', parent=parent_groups[2]), ) for group in groups: @@ -62,6 +70,13 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + def test_q_comments(self): + params = {'q': 'parent'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + params = {'q': 'comment'} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3) + def test_name(self): params = {'name': ['Wireless LAN Group 1', 'Wireless LAN Group 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) diff --git a/netbox/wireless/tests/test_views.py b/netbox/wireless/tests/test_views.py index 51af37364..975f18c0d 100644 --- a/netbox/wireless/tests/test_views.py +++ b/netbox/wireless/tests/test_views.py @@ -16,7 +16,9 @@ class WirelessLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): groups = ( WirelessLANGroup(name='Wireless LAN Group 1', slug='wireless-lan-group-1'), - WirelessLANGroup(name='Wireless LAN Group 2', slug='wireless-lan-group-2'), + WirelessLANGroup( + name='Wireless LAN Group 2', slug='wireless-lan-group-2', comments='LAN Group 2 comment', + ), WirelessLANGroup(name='Wireless LAN Group 3', slug='wireless-lan-group-3'), ) for group in groups: @@ -30,24 +32,26 @@ class WirelessLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase): 'parent': groups[2].pk, 'description': 'A new wireless LAN group', 'tags': [t.pk for t in tags], + 'comments': 'LAN Group X comment', } cls.csv_data = ( - "name,slug,description", - "Wireless LAN Group 4,wireless-lan-group-4,Fourth wireless LAN group", - "Wireless LAN Group 5,wireless-lan-group-5,Fifth wireless LAN group", - "Wireless LAN Group 6,wireless-lan-group-6,Sixth wireless LAN group", + "name,slug,description,comments", + "Wireless LAN Group 4,wireless-lan-group-4,Fourth wireless LAN group,", + "Wireless LAN Group 5,wireless-lan-group-5,Fifth wireless LAN group,", + "Wireless LAN Group 6,wireless-lan-group-6,Sixth wireless LAN group,LAN Group 6 comment", ) cls.csv_update_data = ( - "id,name,description", - f"{groups[0].pk},Wireless LAN Group 7,Fourth wireless LAN group7", - f"{groups[1].pk},Wireless LAN Group 8,Fifth wireless LAN group8", - f"{groups[2].pk},Wireless LAN Group 0,Sixth wireless LAN group9", + "id,name,description,comments", + f"{groups[0].pk},Wireless LAN Group 7,Fourth wireless LAN group7,Group 7 comment", + f"{groups[1].pk},Wireless LAN Group 8,Fifth wireless LAN group8,", + f"{groups[2].pk},Wireless LAN Group 0,Sixth wireless LAN group9,", ) cls.bulk_edit_data = { 'description': 'New description', + 'comments': 'New Comments', } diff --git a/requirements.txt b/requirements.txt index ddd4c1b24..1b91a0ea2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==5.1.8 +Django==5.2.0 django-cors-headers==4.7.0 django-debug-toolbar==5.2.0 django-filter==25.1 @@ -10,15 +10,17 @@ django-prometheus==2.3.1 django-redis==5.4.0 django-rich==1.14.0 django-rq==3.0.1 +django-storages==1.14.6 django-taggit==6.1.0 django-tables2==2.7.5 django-timezone-field==7.1 djangorestframework==3.16.0 drf-spectacular==0.28.0 -drf-spectacular-sidecar==2025.4.1 +drf-spectacular-sidecar==2025.5.1 feedparser==6.0.11 gunicorn==23.0.0 Jinja2==3.1.6 +jsonschema==4.23.0 Markdown==3.8 mkdocs-material==9.6.12 mkdocstrings[python]==0.29.1