From ae447bd18749f1d768f184b2dc6e928b1f958c41 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 24 Oct 2023 05:19:04 -0700 Subject: [PATCH 01/10] 12216 Add color to circuit-type and add to SVG rendering (#14098) * 12216 add color to model * 12216 add forms, serializers for color * 12216 color to detail view, add type to svg * 12216 add color to svg * 12216 review changes --- netbox/circuits/api/serializers.py | 2 +- netbox/circuits/filtersets.py | 2 +- netbox/circuits/forms/bulk_edit.py | 10 +++++++--- netbox/circuits/forms/bulk_import.py | 6 +++++- netbox/circuits/forms/filtersets.py | 11 ++++++++++- netbox/circuits/forms/model_forms.py | 4 ++-- .../migrations/0043_circuittype_color.py | 18 ++++++++++++++++++ netbox/circuits/models/circuits.py | 6 ++++++ netbox/circuits/tables/circuits.py | 3 ++- netbox/dcim/svg/cables.py | 3 +++ netbox/templates/circuits/circuittype.html | 10 ++++++++++ 11 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 netbox/circuits/migrations/0043_circuittype_color.py diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py index f4abda645..5223de339 100644 --- a/netbox/circuits/api/serializers.py +++ b/netbox/circuits/api/serializers.py @@ -85,7 +85,7 @@ class CircuitTypeSerializer(NetBoxModelSerializer): class Meta: model = CircuitType fields = [ - 'id', 'url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count', ] diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py index e28238fea..5c7168318 100644 --- a/netbox/circuits/filtersets.py +++ b/netbox/circuits/filtersets.py @@ -137,7 +137,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet): class Meta: model = CircuitType - fields = ['id', 'name', 'slug', 'description'] + fields = ['id', 'name', 'slug', 'color', 'description'] class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet): diff --git a/netbox/circuits/forms/bulk_edit.py b/netbox/circuits/forms/bulk_edit.py index 1a9366583..5c416bff9 100644 --- a/netbox/circuits/forms/bulk_edit.py +++ b/netbox/circuits/forms/bulk_edit.py @@ -7,7 +7,7 @@ from ipam.models import ASN from netbox.forms import NetBoxModelBulkEditForm from tenancy.models import Tenant from utilities.forms import add_blank_choice -from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField +from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField from utilities.forms.widgets import DatePicker, NumberWithOptions __all__ = ( @@ -91,6 +91,10 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm): class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm): + color = ColorField( + label=_('Color'), + required=False + ) description = forms.CharField( label=_('Description'), max_length=200, @@ -99,9 +103,9 @@ class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm): model = CircuitType fieldsets = ( - (None, ('description',)), + (None, ('color', 'description')), ) - nullable_fields = ('description',) + nullable_fields = ('color', 'description') class CircuitBulkEditForm(NetBoxModelBulkEditForm): diff --git a/netbox/circuits/forms/bulk_import.py b/netbox/circuits/forms/bulk_import.py index d2217b45b..0c30e3cda 100644 --- a/netbox/circuits/forms/bulk_import.py +++ b/netbox/circuits/forms/bulk_import.py @@ -3,6 +3,7 @@ from django import forms from circuits.choices import CircuitStatusChoices from circuits.models import * from dcim.models import Site +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from netbox.forms import NetBoxModelImportForm from tenancy.models import Tenant @@ -64,7 +65,10 @@ class CircuitTypeImportForm(NetBoxModelImportForm): class Meta: model = CircuitType - fields = ('name', 'slug', 'description', 'tags') + fields = ('name', 'slug', 'color', 'description', 'tags') + help_texts = { + 'color': mark_safe(_('RGB color in hexadecimal. Example:') + ' 00ff00'), + } class CircuitImportForm(NetBoxModelImportForm): diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index 1fb239023..830c10d8b 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -7,7 +7,7 @@ from dcim.models import Region, Site, SiteGroup from ipam.models import ASN from netbox.forms import NetBoxModelFilterSetForm from tenancy.forms import TenancyFilterForm, ContactModelFilterForm -from utilities.forms.fields import DynamicModelMultipleChoiceField, TagFilterField +from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField from utilities.forms.widgets import DatePicker, NumberWithOptions __all__ = ( @@ -97,8 +97,17 @@ class ProviderNetworkFilterForm(NetBoxModelFilterSetForm): class CircuitTypeFilterForm(NetBoxModelFilterSetForm): model = CircuitType + fieldsets = ( + (None, ('q', 'filter_id', 'tag')), + (_('Attributes'), ('color',)), + ) tag = TagFilterField(model) + color = ColorField( + label=_('Color'), + required=False + ) + class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm): model = Circuit diff --git a/netbox/circuits/forms/model_forms.py b/netbox/circuits/forms/model_forms.py index 8a540032e..0809cb2f4 100644 --- a/netbox/circuits/forms/model_forms.py +++ b/netbox/circuits/forms/model_forms.py @@ -76,14 +76,14 @@ class CircuitTypeForm(NetBoxModelForm): fieldsets = ( (_('Circuit Type'), ( - 'name', 'slug', 'description', 'tags', + 'name', 'slug', 'color', 'description', 'tags', )), ) class Meta: model = CircuitType fields = [ - 'name', 'slug', 'description', 'tags', + 'name', 'slug', 'color', 'description', 'tags', ] diff --git a/netbox/circuits/migrations/0043_circuittype_color.py b/netbox/circuits/migrations/0043_circuittype_color.py new file mode 100644 index 000000000..6c4dffeb6 --- /dev/null +++ b/netbox/circuits/migrations/0043_circuittype_color.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2023-10-20 21:25 + +from django.db import migrations +import utilities.fields + + +class Migration(migrations.Migration): + dependencies = [ + ('circuits', '0042_provideraccount'), + ] + + operations = [ + migrations.AddField( + model_name='circuittype', + name='color', + field=utilities.fields.ColorField(blank=True, max_length=6), + ), + ] diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 0322b67c6..4dc775364 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -7,6 +7,7 @@ from circuits.choices import * from dcim.models import CabledObjectModel from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel from netbox.models.features import ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ImageAttachmentsMixin, TagsMixin +from utilities.fields import ColorField __all__ = ( 'Circuit', @@ -20,6 +21,11 @@ class CircuitType(OrganizationalModel): Circuits can be organized by their functional role. For example, a user might wish to define CircuitTypes named "Long Haul," "Metro," or "Out-of-Band". """ + color = ColorField( + verbose_name=_('color'), + blank=True + ) + def get_absolute_url(self): return reverse('circuits:circuittype', args=[self.pk]) diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index 6a05983e6..6ae727eca 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -28,6 +28,7 @@ class CircuitTypeTable(NetBoxTable): linkify=True, verbose_name=_('Name'), ) + color = columns.ColorColumn() tags = columns.TagColumn( url_name='circuits:circuittype_list' ) @@ -40,7 +41,7 @@ class CircuitTypeTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = CircuitType fields = ( - 'pk', 'id', 'name', 'circuit_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions', + 'pk', 'id', 'name', 'circuit_count', 'color', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions', ) default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug') diff --git a/netbox/dcim/svg/cables.py b/netbox/dcim/svg/cables.py index acc4fcad9..31e090078 100644 --- a/netbox/dcim/svg/cables.py +++ b/netbox/dcim/svg/cables.py @@ -159,6 +159,7 @@ class CableTraceSVG: labels.append(location_label) elif instance._meta.model_name == 'circuit': labels[0] = f'Circuit {instance}' + labels.append(instance.type) labels.append(instance.provider) if instance.description: labels.append(instance.description) @@ -181,6 +182,8 @@ class CableTraceSVG: if hasattr(instance, 'role'): # Device return instance.role.color + elif instance._meta.model_name == 'circuit' and instance.type.color: + return instance.type.color else: # Other parent object return 'e0e0e0' diff --git a/netbox/templates/circuits/circuittype.html b/netbox/templates/circuits/circuittype.html index b8b08baf0..407ee4042 100644 --- a/netbox/templates/circuits/circuittype.html +++ b/netbox/templates/circuits/circuittype.html @@ -29,6 +29,16 @@ {% trans "Description" %} {{ object.description|placeholder }} + + {% trans "Color" %} + + {% if object.color %} +   + {% else %} + {{ ''|placeholder }} + {% endif %} + + From 7274e75b26b6153a3a22c74f214de52bba53c1a8 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 24 Oct 2023 08:29:24 -0700 Subject: [PATCH 02/10] 13230 Allow Devices to be excluded from Rack utilization (#14099) * 13230 add exclusion flag to device type * 13230 forms, detail views * 13230 add tests * 13230 extraneous model field * 13230 extraneous form field * Update netbox/dcim/forms/bulk_edit.py Co-authored-by: Jeremy Stretch * 13230 review feedback --------- Co-authored-by: Jeremy Stretch --- netbox/dcim/api/serializers.py | 6 ++-- netbox/dcim/filtersets.py | 3 +- netbox/dcim/forms/bulk_edit.py | 10 +++++- netbox/dcim/forms/bulk_import.py | 4 +-- netbox/dcim/forms/model_forms.py | 9 ++--- ...182_devicetype_exclude_from_utilization.py | 17 ++++++++++ netbox/dcim/models/devices.py | 5 +++ netbox/dcim/models/racks.py | 8 +++-- netbox/dcim/tables/devicetypes.py | 7 ++-- netbox/dcim/tests/test_models.py | 34 +++++++++++++++++++ netbox/templates/dcim/devicetype.html | 4 +++ 11 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 netbox/dcim/migrations/0182_devicetype_exclude_from_utilization.py diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index b43611dad..32dcdc5bb 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -343,9 +343,9 @@ class DeviceTypeSerializer(NetBoxModelSerializer): model = DeviceType fields = [ 'id', 'url', 'display', 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', - 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', - 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', - 'console_port_template_count', 'console_server_port_template_count', 'power_port_template_count', + 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', + 'front_image', 'rear_image', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', + 'device_count', 'console_port_template_count', 'console_server_port_template_count', 'power_port_template_count', 'power_outlet_template_count', 'interface_template_count', 'front_port_template_count', 'rear_port_template_count', 'device_bay_template_count', 'module_bay_template_count', 'inventory_item_template_count', diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index d600667d7..c65110d9a 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -496,7 +496,8 @@ class DeviceTypeFilterSet(NetBoxModelFilterSet): class Meta: model = DeviceType fields = [ - 'id', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', + 'id', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', + 'airflow', 'weight', 'weight_unit', ] def search(self, queryset, name, value): diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index cacf1f72b..9c64d8a19 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/netbox/dcim/forms/bulk_edit.py @@ -420,6 +420,11 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm): widget=BulkEditNullBooleanSelect(), label=_('Is full depth') ) + exclude_from_utilization = forms.NullBooleanField( + required=False, + widget=BulkEditNullBooleanSelect(), + label=_('Exclude from utilization') + ) airflow = forms.ChoiceField( label=_('Airflow'), choices=add_blank_choice(DeviceAirflowChoices), @@ -445,7 +450,10 @@ class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm): model = DeviceType fieldsets = ( - (_('Device Type'), ('manufacturer', 'default_platform', 'part_number', 'u_height', 'is_full_depth', 'airflow', 'description')), + (_('Device Type'), ( + 'manufacturer', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth', + 'airflow', 'description', + )), (_('Weight'), ('weight', 'weight_unit')), ) nullable_fields = ('part_number', 'airflow', 'weight', 'weight_unit', 'description', 'comments') diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index e41e875e4..d63873b59 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -335,8 +335,8 @@ class DeviceTypeImportForm(NetBoxModelImportForm): class Meta: model = DeviceType fields = [ - 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', - 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'comments', 'tags', + 'manufacturer', 'default_platform', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization', + 'is_full_depth', 'subdevice_role', 'airflow', 'description', 'weight', 'weight_unit', 'comments', 'tags', ] diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 93e214598..3d626d201 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -302,7 +302,8 @@ class DeviceTypeForm(NetBoxModelForm): fieldsets = ( (_('Device Type'), ('manufacturer', 'model', 'slug', 'default_platform', 'description', 'tags')), (_('Chassis'), ( - 'u_height', 'is_full_depth', 'part_number', 'subdevice_role', 'airflow', 'weight', 'weight_unit', + 'u_height', 'exclude_from_utilization', 'is_full_depth', 'part_number', 'subdevice_role', 'airflow', + 'weight', 'weight_unit', )), (_('Images'), ('front_image', 'rear_image')), ) @@ -310,9 +311,9 @@ class DeviceTypeForm(NetBoxModelForm): class Meta: model = DeviceType fields = [ - 'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'is_full_depth', - 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', 'description', - 'comments', 'tags', + 'manufacturer', 'model', 'slug', 'default_platform', 'part_number', 'u_height', 'exclude_from_utilization', + 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit', 'front_image', 'rear_image', + 'description', 'comments', 'tags', ] widgets = { 'front_image': ClearableFileInput(attrs={ diff --git a/netbox/dcim/migrations/0182_devicetype_exclude_from_utilization.py b/netbox/dcim/migrations/0182_devicetype_exclude_from_utilization.py new file mode 100644 index 000000000..6943387c5 --- /dev/null +++ b/netbox/dcim/migrations/0182_devicetype_exclude_from_utilization.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.5 on 2023-10-20 22:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('dcim', '0181_rename_device_role_device_role'), + ] + + operations = [ + migrations.AddField( + model_name='devicetype', + name='exclude_from_utilization', + field=models.BooleanField(default=False), + ), + ] diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index c9ebf898d..943bf318c 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -106,6 +106,11 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): default=1.0, verbose_name=_('height (U)') ) + exclude_from_utilization = models.BooleanField( + default=False, + verbose_name=_('exclude from utilization'), + help_text=_('Exclude from rack utilization calculations.') + ) is_full_depth = models.BooleanField( default=True, verbose_name=_('is full depth'), diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index ef0dde4da..ab1027d1b 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -357,7 +357,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, PrimaryModel, WeightMixin): return [u for u in elevation.values()] - def get_available_units(self, u_height=1, rack_face=None, exclude=None): + def get_available_units(self, u_height=1, rack_face=None, exclude=None, ignore_excluded_devices=False): """ Return a list of units within the rack available to accommodate a device of a given U height (default 1). Optionally exclude one or more devices when calculating empty units (needed when moving a device from one @@ -366,9 +366,13 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, PrimaryModel, WeightMixin): :param u_height: Minimum number of contiguous free units required :param rack_face: The face of the rack (front or rear) required; 'None' if device is full depth :param exclude: List of devices IDs to exclude (useful when moving a device within a rack) + :param ignore_excluded_devices: Ignore devices that are marked to exclude from utilization calculations """ # Gather all devices which consume U space within the rack devices = self.devices.prefetch_related('device_type').filter(position__gte=1) + if ignore_excluded_devices: + devices = devices.exclude(device_type__exclude_from_utilization=True) + if exclude is not None: devices = devices.exclude(pk__in=exclude) @@ -453,7 +457,7 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, PrimaryModel, WeightMixin): """ # Determine unoccupied units total_units = len(list(self.units)) - available_units = self.get_available_units(u_height=0.5) + available_units = self.get_available_units(u_height=0.5, ignore_excluded_devices=True) # Remove reserved units for ru in self.get_reserved_units(): diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index 7d8884fc1..fad238c6e 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -98,6 +98,7 @@ class DeviceTypeTable(NetBoxTable): verbose_name=_('U Height'), template_code='{{ value|floatformat }}' ) + exclude_from_utilization = columns.BooleanColumn() weight = columns.TemplateColumn( verbose_name=_('Weight'), template_code=WEIGHT, @@ -142,9 +143,9 @@ class DeviceTypeTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = models.DeviceType fields = ( - 'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', 'is_full_depth', - 'subdevice_role', 'airflow', 'weight', 'description', 'comments', 'instance_count', 'tags', 'created', - 'last_updated', + 'pk', 'id', 'model', 'manufacturer', 'default_platform', 'slug', 'part_number', 'u_height', + 'exclude_from_utilization', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', + 'description', 'comments', 'instance_count', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count', diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index 2e5ae0d5c..741a615d4 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -238,6 +238,40 @@ class RackTestCase(TestCase): # Check that Device1 is now assigned to Site B self.assertEqual(Device.objects.get(pk=device1.pk).site, site_b) + def test_utilization(self): + site = Site.objects.first() + rack = Rack.objects.first() + + Device( + name='Device 1', + role=DeviceRole.objects.first(), + device_type=DeviceType.objects.first(), + site=site, + rack=rack, + position=1 + ).save() + rack.refresh_from_db() + self.assertEqual(rack.get_utilization(), 1 / 42 * 100) + + # create device excluded from utilization calculations + dt = DeviceType.objects.create( + manufacturer=Manufacturer.objects.first(), + model='Device Type 4', + slug='device-type-4', + u_height=1, + exclude_from_utilization=True + ) + Device( + name='Device 2', + role=DeviceRole.objects.first(), + device_type=dt, + site=site, + rack=rack, + position=5 + ).save() + rack.refresh_from_db() + self.assertEqual(rack.get_utilization(), 1 / 42 * 100) + class DeviceTestCase(TestCase): diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 419ab7f00..35b089664 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -40,6 +40,10 @@ {% trans "Height (U" %}) {{ object.u_height|floatformat }} + + {% trans "Exclude From Utilization" %}) + {% checkmark object.exclude_from_utilization %} + {% trans "Full Depth" %} {% checkmark object.is_full_depth %} From 30ce9edf1c44ae181b376461bcd4c29f115fc80b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 24 Oct 2023 11:35:53 -0400 Subject: [PATCH 03/10] Closes #13381: Enable plugins to register custom data backends (#14095) * Initial work on #13381 * Fix backend type display in table column * Fix data source type choices during bulk edit * Misc cleanup * Move backend utils from core app to netbox * Move backend type validation from serializer to model --- docs/plugins/development/data-backends.md | 23 ++++++++ docs/plugins/development/index.md | 1 + mkdocs.yml | 1 + netbox/core/api/serializers.py | 3 +- netbox/core/choices.py | 12 ---- netbox/core/data_backends.py | 59 ++++--------------- netbox/core/filtersets.py | 3 +- netbox/core/forms/bulk_edit.py | 8 +-- netbox/core/forms/filtersets.py | 3 +- netbox/core/forms/model_forms.py | 19 +++--- .../0006_datasource_type_remove_choices.py | 18 ++++++ netbox/core/models/data.py | 21 +++---- netbox/core/tables/columns.py | 20 +++++++ netbox/core/tables/data.py | 9 +-- netbox/core/tests/test_api.py | 15 +++-- netbox/core/tests/test_filtersets.py | 14 ++--- netbox/core/tests/test_views.py | 19 +++--- netbox/netbox/data_backends.py | 53 +++++++++++++++++ netbox/netbox/plugins/__init__.py | 8 +++ .../tests/dummy_plugin/data_backends.py | 18 ++++++ netbox/netbox/tests/test_plugins.py | 8 +++ netbox/netbox/utils.py | 26 ++++++++ netbox/templates/core/datasource.html | 2 +- 23 files changed, 250 insertions(+), 113 deletions(-) create mode 100644 docs/plugins/development/data-backends.md create mode 100644 netbox/core/migrations/0006_datasource_type_remove_choices.py create mode 100644 netbox/core/tables/columns.py create mode 100644 netbox/netbox/data_backends.py create mode 100644 netbox/netbox/tests/dummy_plugin/data_backends.py create mode 100644 netbox/netbox/utils.py diff --git a/docs/plugins/development/data-backends.md b/docs/plugins/development/data-backends.md new file mode 100644 index 000000000..feffa5bed --- /dev/null +++ b/docs/plugins/development/data-backends.md @@ -0,0 +1,23 @@ +# Data Backends + +[Data sources](../../models/core/datasource.md) can be defined to reference data which exists on systems of record outside NetBox, such as a git repository or Amazon S3 bucket. Plugins can register their own backend classes to introduce support for additional resource types. This is done by subclassing NetBox's `DataBackend` class. + +```python title="data_backends.py" +from netbox.data_backends import DataBackend + +class MyDataBackend(DataBackend): + name = 'mybackend' + label = 'My Backend' + ... +``` + +To register one or more data backends with NetBox, define a list named `backends` at the end of this file: + +```python title="data_backends.py" +backends = [MyDataBackend] +``` + +!!! tip + The path to the list of search indexes can be modified by setting `data_backends` in the PluginConfig instance. + +::: core.data_backends.DataBackend diff --git a/docs/plugins/development/index.md b/docs/plugins/development/index.md index dcbad9d8d..d3f50a0fb 100644 --- a/docs/plugins/development/index.md +++ b/docs/plugins/development/index.md @@ -109,6 +109,7 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i | `middleware` | A list of middleware classes to append after NetBox's build-in middleware | | `queues` | A list of custom background task queues to create | | `search_extensions` | The dotted path to the list of search index classes (default: `search.indexes`) | +| `data_backends` | The dotted path to the list of data source backend classes (default: `data_backends.backends`) | | `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) | | `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) | | `graphql_schema` | The dotted path to the plugin's GraphQL schema class, if any (default: `graphql.schema`) | diff --git a/mkdocs.yml b/mkdocs.yml index cc16434de..3e61f922a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -136,6 +136,7 @@ nav: - Forms: 'plugins/development/forms.md' - Filters & Filter Sets: 'plugins/development/filtersets.md' - Search: 'plugins/development/search.md' + - Data Backends: 'plugins/development/data-backends.md' - REST API: 'plugins/development/rest-api.md' - GraphQL API: 'plugins/development/graphql-api.md' - Background Tasks: 'plugins/development/background-tasks.md' diff --git a/netbox/core/api/serializers.py b/netbox/core/api/serializers.py index 4117a609c..0d743d952 100644 --- a/netbox/core/api/serializers.py +++ b/netbox/core/api/serializers.py @@ -4,6 +4,7 @@ from core.choices import * from core.models import * from netbox.api.fields import ChoiceField, ContentTypeField from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer +from netbox.utils import get_data_backend_choices from users.api.nested_serializers import NestedUserSerializer from .nested_serializers import * @@ -19,7 +20,7 @@ class DataSourceSerializer(NetBoxModelSerializer): view_name='core-api:datasource-detail' ) type = ChoiceField( - choices=DataSourceTypeChoices + choices=get_data_backend_choices() ) status = ChoiceField( choices=DataSourceStatusChoices, diff --git a/netbox/core/choices.py b/netbox/core/choices.py index b5d9d0d90..8d7050414 100644 --- a/netbox/core/choices.py +++ b/netbox/core/choices.py @@ -7,18 +7,6 @@ from utilities.choices import ChoiceSet # Data sources # -class DataSourceTypeChoices(ChoiceSet): - LOCAL = 'local' - GIT = 'git' - AMAZON_S3 = 'amazon-s3' - - CHOICES = ( - (LOCAL, _('Local'), 'gray'), - (GIT, 'Git', 'blue'), - (AMAZON_S3, 'Amazon S3', 'blue'), - ) - - class DataSourceStatusChoices(ChoiceSet): NEW = 'new' QUEUED = 'queued' diff --git a/netbox/core/data_backends.py b/netbox/core/data_backends.py index 82b3962dd..9ff0b4d63 100644 --- a/netbox/core/data_backends.py +++ b/netbox/core/data_backends.py @@ -10,61 +10,24 @@ from django import forms from django.conf import settings from django.utils.translation import gettext as _ -from netbox.registry import registry -from .choices import DataSourceTypeChoices +from netbox.data_backends import DataBackend +from netbox.utils import register_data_backend from .exceptions import SyncError __all__ = ( - 'LocalBackend', 'GitBackend', + 'LocalBackend', 'S3Backend', ) logger = logging.getLogger('netbox.data_backends') -def register_backend(name): - """ - Decorator for registering a DataBackend class. - """ - - def _wrapper(cls): - registry['data_backends'][name] = cls - return cls - - return _wrapper - - -class DataBackend: - parameters = {} - sensitive_parameters = [] - - # Prevent Django's template engine from calling the backend - # class when referenced via DataSource.backend_class - do_not_call_in_templates = True - - def __init__(self, url, **kwargs): - self.url = url - self.params = kwargs - self.config = self.init_config() - - def init_config(self): - """ - Hook to initialize the instance's configuration. - """ - return - - @property - def url_scheme(self): - return urlparse(self.url).scheme.lower() - - @contextmanager - def fetch(self): - raise NotImplemented() - - -@register_backend(DataSourceTypeChoices.LOCAL) +@register_data_backend() class LocalBackend(DataBackend): + name = 'local' + label = _('Local') + is_local = True @contextmanager def fetch(self): @@ -74,8 +37,10 @@ class LocalBackend(DataBackend): yield local_path -@register_backend(DataSourceTypeChoices.GIT) +@register_data_backend() class GitBackend(DataBackend): + name = 'git' + label = 'Git' parameters = { 'username': forms.CharField( required=False, @@ -144,8 +109,10 @@ class GitBackend(DataBackend): local_path.cleanup() -@register_backend(DataSourceTypeChoices.AMAZON_S3) +@register_data_backend() class S3Backend(DataBackend): + name = 'amazon-s3' + label = 'Amazon S3' parameters = { 'aws_access_key_id': forms.CharField( label=_('AWS access key ID'), diff --git a/netbox/core/filtersets.py b/netbox/core/filtersets.py index 62a58086a..410e2e80c 100644 --- a/netbox/core/filtersets.py +++ b/netbox/core/filtersets.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext as _ import django_filters from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet +from netbox.utils import get_data_backend_choices from .choices import * from .models import * @@ -16,7 +17,7 @@ __all__ = ( class DataSourceFilterSet(NetBoxModelFilterSet): type = django_filters.MultipleChoiceFilter( - choices=DataSourceTypeChoices, + choices=get_data_backend_choices, null_value=None ) status = django_filters.MultipleChoiceFilter( diff --git a/netbox/core/forms/bulk_edit.py b/netbox/core/forms/bulk_edit.py index a4ecd646f..dcc92c6f0 100644 --- a/netbox/core/forms/bulk_edit.py +++ b/netbox/core/forms/bulk_edit.py @@ -1,10 +1,9 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from core.choices import DataSourceTypeChoices from core.models import * from netbox.forms import NetBoxModelBulkEditForm -from utilities.forms import add_blank_choice +from netbox.utils import get_data_backend_choices from utilities.forms.fields import CommentField from utilities.forms.widgets import BulkEditNullBooleanSelect @@ -16,9 +15,8 @@ __all__ = ( class DataSourceBulkEditForm(NetBoxModelBulkEditForm): type = forms.ChoiceField( label=_('Type'), - choices=add_blank_choice(DataSourceTypeChoices), - required=False, - initial='' + choices=get_data_backend_choices, + required=False ) enabled = forms.NullBooleanField( required=False, diff --git a/netbox/core/forms/filtersets.py b/netbox/core/forms/filtersets.py index f7a6f3595..4d0acbb77 100644 --- a/netbox/core/forms/filtersets.py +++ b/netbox/core/forms/filtersets.py @@ -8,6 +8,7 @@ from core.models import * from extras.forms.mixins import SavedFiltersMixin from extras.utils import FeatureQuery from netbox.forms import NetBoxModelFilterSetForm +from netbox.utils import get_data_backend_choices from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm from utilities.forms.fields import ContentTypeChoiceField, DynamicModelMultipleChoiceField from utilities.forms.widgets import APISelectMultiple, DateTimePicker @@ -27,7 +28,7 @@ class DataSourceFilterForm(NetBoxModelFilterSetForm): ) type = forms.MultipleChoiceField( label=_('Type'), - choices=DataSourceTypeChoices, + choices=get_data_backend_choices, required=False ) status = forms.MultipleChoiceField( diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index 01d5474c6..e3184acf6 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -7,6 +7,7 @@ from core.forms.mixins import SyncedDataMixin from core.models import * from netbox.forms import NetBoxModelForm from netbox.registry import registry +from netbox.utils import get_data_backend_choices from utilities.forms import get_field_value from utilities.forms.fields import CommentField from utilities.forms.widgets import HTMXSelect @@ -18,6 +19,10 @@ __all__ = ( class DataSourceForm(NetBoxModelForm): + type = forms.ChoiceField( + choices=get_data_backend_choices, + widget=HTMXSelect() + ) comments = CommentField() class Meta: @@ -26,7 +31,6 @@ class DataSourceForm(NetBoxModelForm): 'name', 'type', 'source_url', 'enabled', 'description', 'comments', 'ignore_rules', 'tags', ] widgets = { - 'type': HTMXSelect(), 'ignore_rules': forms.Textarea( attrs={ 'rows': 5, @@ -56,12 +60,13 @@ class DataSourceForm(NetBoxModelForm): # Add backend-specific form fields self.backend_fields = [] - for name, form_field in backend.parameters.items(): - field_name = f'backend_{name}' - self.backend_fields.append(field_name) - self.fields[field_name] = copy.copy(form_field) - if self.instance and self.instance.parameters: - self.fields[field_name].initial = self.instance.parameters.get(name) + if backend: + for name, form_field in backend.parameters.items(): + field_name = f'backend_{name}' + self.backend_fields.append(field_name) + self.fields[field_name] = copy.copy(form_field) + if self.instance and self.instance.parameters: + self.fields[field_name].initial = self.instance.parameters.get(name) def save(self, *args, **kwargs): diff --git a/netbox/core/migrations/0006_datasource_type_remove_choices.py b/netbox/core/migrations/0006_datasource_type_remove_choices.py new file mode 100644 index 000000000..0ad8d8854 --- /dev/null +++ b/netbox/core/migrations/0006_datasource_type_remove_choices.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.6 on 2023-10-20 17:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_job_created_auto_now'), + ] + + operations = [ + migrations.AlterField( + model_name='datasource', + name='type', + field=models.CharField(max_length=50), + ), + ] diff --git a/netbox/core/models/data.py b/netbox/core/models/data.py index 54a43c7ef..fb764134a 100644 --- a/netbox/core/models/data.py +++ b/netbox/core/models/data.py @@ -45,9 +45,7 @@ class DataSource(JobsMixin, PrimaryModel): ) type = models.CharField( verbose_name=_('type'), - max_length=50, - choices=DataSourceTypeChoices, - default=DataSourceTypeChoices.LOCAL + max_length=50 ) source_url = models.CharField( max_length=200, @@ -96,8 +94,9 @@ class DataSource(JobsMixin, PrimaryModel): def docs_url(self): return f'{settings.STATIC_URL}docs/models/{self._meta.app_label}/{self._meta.model_name}/' - def get_type_color(self): - return DataSourceTypeChoices.colors.get(self.type) + def get_type_display(self): + if backend := registry['data_backends'].get(self.type): + return backend.label def get_status_color(self): return DataSourceStatusChoices.colors.get(self.status) @@ -110,10 +109,6 @@ class DataSource(JobsMixin, PrimaryModel): def backend_class(self): return registry['data_backends'].get(self.type) - @property - def is_local(self): - return self.type == DataSourceTypeChoices.LOCAL - @property def ready_for_sync(self): return self.enabled and self.status not in ( @@ -123,8 +118,14 @@ class DataSource(JobsMixin, PrimaryModel): def clean(self): + # Validate data backend type + if self.type and self.type not in registry['data_backends']: + raise ValidationError({ + 'type': _("Unknown backend type: {type}".format(type=self.type)) + }) + # Ensure URL scheme matches selected type - if self.type == DataSourceTypeChoices.LOCAL and self.url_scheme not in ('file', ''): + if self.backend_class.is_local and self.url_scheme not in ('file', ''): raise ValidationError({ 'source_url': f"URLs for local sources must start with file:// (or specify no scheme)" }) diff --git a/netbox/core/tables/columns.py b/netbox/core/tables/columns.py new file mode 100644 index 000000000..93f1e3901 --- /dev/null +++ b/netbox/core/tables/columns.py @@ -0,0 +1,20 @@ +import django_tables2 as tables + +from netbox.registry import registry + +__all__ = ( + 'BackendTypeColumn', +) + + +class BackendTypeColumn(tables.Column): + """ + Display a data backend type. + """ + def render(self, value): + if backend := registry['data_backends'].get(value): + return backend.label + return value + + def value(self, value): + return value diff --git a/netbox/core/tables/data.py b/netbox/core/tables/data.py index 1ecc42369..4059ea9bc 100644 --- a/netbox/core/tables/data.py +++ b/netbox/core/tables/data.py @@ -3,6 +3,7 @@ import django_tables2 as tables from core.models import * from netbox.tables import NetBoxTable, columns +from .columns import BackendTypeColumn __all__ = ( 'DataFileTable', @@ -15,8 +16,8 @@ class DataSourceTable(NetBoxTable): verbose_name=_('Name'), linkify=True ) - type = columns.ChoiceFieldColumn( - verbose_name=_('Type'), + type = BackendTypeColumn( + verbose_name=_('Type') ) status = columns.ChoiceFieldColumn( verbose_name=_('Status'), @@ -34,8 +35,8 @@ class DataSourceTable(NetBoxTable): 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', 'comments', 'parameters', + 'created', 'last_updated', 'file_count', ) default_columns = ('pk', 'name', 'type', 'status', 'enabled', 'description', 'file_count') diff --git a/netbox/core/tests/test_api.py b/netbox/core/tests/test_api.py index dc6d6a5ce..cd25761f0 100644 --- a/netbox/core/tests/test_api.py +++ b/netbox/core/tests/test_api.py @@ -2,7 +2,6 @@ from django.urls import reverse from django.utils import timezone from utilities.testing import APITestCase, APIViewTestCases -from ..choices import * from ..models import * @@ -26,26 +25,26 @@ class DataSourceTest(APIViewTestCases.APIViewTestCase): @classmethod def setUpTestData(cls): data_sources = ( - DataSource(name='Data Source 1', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source1/'), - DataSource(name='Data Source 2', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source2/'), - DataSource(name='Data Source 3', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source3/'), + DataSource(name='Data Source 1', type='local', source_url='file:///var/tmp/source1/'), + DataSource(name='Data Source 2', type='local', source_url='file:///var/tmp/source2/'), + DataSource(name='Data Source 3', type='local', source_url='file:///var/tmp/source3/'), ) DataSource.objects.bulk_create(data_sources) cls.create_data = [ { 'name': 'Data Source 4', - 'type': DataSourceTypeChoices.GIT, + 'type': 'git', 'source_url': 'https://example.com/git/source4' }, { 'name': 'Data Source 5', - 'type': DataSourceTypeChoices.GIT, + 'type': 'git', 'source_url': 'https://example.com/git/source5' }, { 'name': 'Data Source 6', - 'type': DataSourceTypeChoices.GIT, + 'type': 'git', 'source_url': 'https://example.com/git/source6' }, ] @@ -63,7 +62,7 @@ class DataFileTest( def setUpTestData(cls): datasource = DataSource.objects.create( name='Data Source 1', - type=DataSourceTypeChoices.LOCAL, + type='local', source_url='file:///var/tmp/source1/' ) diff --git a/netbox/core/tests/test_filtersets.py b/netbox/core/tests/test_filtersets.py index e1e916f70..2f60c7522 100644 --- a/netbox/core/tests/test_filtersets.py +++ b/netbox/core/tests/test_filtersets.py @@ -18,21 +18,21 @@ class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests): data_sources = ( DataSource( name='Data Source 1', - type=DataSourceTypeChoices.LOCAL, + type='local', source_url='file:///var/tmp/source1/', status=DataSourceStatusChoices.NEW, enabled=True ), DataSource( name='Data Source 2', - type=DataSourceTypeChoices.LOCAL, + type='local', source_url='file:///var/tmp/source2/', status=DataSourceStatusChoices.SYNCING, enabled=True ), DataSource( name='Data Source 3', - type=DataSourceTypeChoices.GIT, + type='git', source_url='https://example.com/git/source3', status=DataSourceStatusChoices.COMPLETED, enabled=False @@ -45,7 +45,7 @@ class DataSourceTestCase(TestCase, ChangeLoggedFilterSetTests): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_type(self): - params = {'type': [DataSourceTypeChoices.LOCAL]} + params = {'type': ['local']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_enabled(self): @@ -66,9 +66,9 @@ class DataFileTestCase(TestCase, ChangeLoggedFilterSetTests): @classmethod def setUpTestData(cls): data_sources = ( - DataSource(name='Data Source 1', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source1/'), - DataSource(name='Data Source 2', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source2/'), - DataSource(name='Data Source 3', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source3/'), + DataSource(name='Data Source 1', type='local', source_url='file:///var/tmp/source1/'), + DataSource(name='Data Source 2', type='local', source_url='file:///var/tmp/source2/'), + DataSource(name='Data Source 3', type='local', source_url='file:///var/tmp/source3/'), ) DataSource.objects.bulk_create(data_sources) diff --git a/netbox/core/tests/test_views.py b/netbox/core/tests/test_views.py index 4a50a8d05..16d07f376 100644 --- a/netbox/core/tests/test_views.py +++ b/netbox/core/tests/test_views.py @@ -1,7 +1,6 @@ from django.utils import timezone from utilities.testing import ViewTestCases, create_tags -from ..choices import * from ..models import * @@ -11,9 +10,9 @@ class DataSourceTestCase(ViewTestCases.PrimaryObjectViewTestCase): @classmethod def setUpTestData(cls): data_sources = ( - DataSource(name='Data Source 1', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source1/'), - DataSource(name='Data Source 2', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source2/'), - DataSource(name='Data Source 3', type=DataSourceTypeChoices.LOCAL, source_url='file:///var/tmp/source3/'), + DataSource(name='Data Source 1', type='local', source_url='file:///var/tmp/source1/'), + DataSource(name='Data Source 2', type='local', source_url='file:///var/tmp/source2/'), + DataSource(name='Data Source 3', type='local', source_url='file:///var/tmp/source3/'), ) DataSource.objects.bulk_create(data_sources) @@ -21,7 +20,7 @@ class DataSourceTestCase(ViewTestCases.PrimaryObjectViewTestCase): cls.form_data = { 'name': 'Data Source X', - 'type': DataSourceTypeChoices.GIT, + 'type': 'git', 'source_url': 'http:///exmaple/com/foo/bar/', 'description': 'Something', 'comments': 'Foo bar baz', @@ -29,10 +28,10 @@ class DataSourceTestCase(ViewTestCases.PrimaryObjectViewTestCase): } cls.csv_data = ( - f"name,type,source_url,enabled", - f"Data Source 4,{DataSourceTypeChoices.LOCAL},file:///var/tmp/source4/,true", - f"Data Source 5,{DataSourceTypeChoices.LOCAL},file:///var/tmp/source4/,true", - f"Data Source 6,{DataSourceTypeChoices.GIT},http:///exmaple/com/foo/bar/,false", + "name,type,source_url,enabled", + "Data Source 4,local,file:///var/tmp/source4/,true", + "Data Source 5,local,file:///var/tmp/source4/,true", + "Data Source 6,git,http:///exmaple/com/foo/bar/,false", ) cls.csv_update_data = ( @@ -60,7 +59,7 @@ class DataFileTestCase( def setUpTestData(cls): datasource = DataSource.objects.create( name='Data Source 1', - type=DataSourceTypeChoices.LOCAL, + type='local', source_url='file:///var/tmp/source1/' ) diff --git a/netbox/netbox/data_backends.py b/netbox/netbox/data_backends.py new file mode 100644 index 000000000..d5bab75c1 --- /dev/null +++ b/netbox/netbox/data_backends.py @@ -0,0 +1,53 @@ +from contextlib import contextmanager +from urllib.parse import urlparse + +__all__ = ( + 'DataBackend', +) + + +class DataBackend: + """ + A data backend represents a specific system of record for data, such as a git repository or Amazon S3 bucket. + + Attributes: + name: The identifier under which this backend will be registered in NetBox + label: The human-friendly name for this backend + is_local: A boolean indicating whether this backend accesses local data + parameters: A dictionary mapping configuration form field names to their classes + sensitive_parameters: An iterable of field names for which the values should not be displayed to the user + """ + is_local = False + parameters = {} + sensitive_parameters = [] + + # Prevent Django's template engine from calling the backend + # class when referenced via DataSource.backend_class + do_not_call_in_templates = True + + def __init__(self, url, **kwargs): + self.url = url + self.params = kwargs + self.config = self.init_config() + + def init_config(self): + """ + A hook to initialize the instance's configuration. The data returned by this method is assigned to the + instance's `config` attribute upon initialization, which can be referenced by the `fetch()` method. + """ + return + + @property + def url_scheme(self): + return urlparse(self.url).scheme.lower() + + @contextmanager + def fetch(self): + """ + A context manager which performs the following: + + 1. Handles all setup and synchronization + 2. Yields the local path at which data has been replicated + 3. Performs any necessary cleanup + """ + raise NotImplemented() diff --git a/netbox/netbox/plugins/__init__.py b/netbox/netbox/plugins/__init__.py index f60462f3d..8b6901b7a 100644 --- a/netbox/netbox/plugins/__init__.py +++ b/netbox/netbox/plugins/__init__.py @@ -8,6 +8,7 @@ from packaging import version from netbox.registry import registry from netbox.search import register_search +from netbox.utils import register_data_backend from .navigation import * from .registration import * from .templates import * @@ -24,6 +25,7 @@ registry['plugins'].update({ DEFAULT_RESOURCE_PATHS = { 'search_indexes': 'search.indexes', + 'data_backends': 'data_backends.backends', 'graphql_schema': 'graphql.schema', 'menu': 'navigation.menu', 'menu_items': 'navigation.menu_items', @@ -70,6 +72,7 @@ class PluginConfig(AppConfig): # Optional plugin resources search_indexes = None + data_backends = None graphql_schema = None menu = None menu_items = None @@ -98,6 +101,11 @@ class PluginConfig(AppConfig): for idx in search_indexes: register_search(idx) + # Register data backends (if defined) + data_backends = self._load_resource('data_backends') or [] + for backend in data_backends: + register_data_backend()(backend) + # Register template content (if defined) if template_extensions := self._load_resource('template_extensions'): register_template_extensions(template_extensions) diff --git a/netbox/netbox/tests/dummy_plugin/data_backends.py b/netbox/netbox/tests/dummy_plugin/data_backends.py new file mode 100644 index 000000000..9b63e51c6 --- /dev/null +++ b/netbox/netbox/tests/dummy_plugin/data_backends.py @@ -0,0 +1,18 @@ +from contextlib import contextmanager + +from netbox.data_backends import DataBackend + + +class DummyBackend(DataBackend): + name = 'dummy' + label = 'Dummy' + is_local = True + + @contextmanager + def fetch(self): + yield '/tmp' + + +backends = ( + DummyBackend, +) diff --git a/netbox/netbox/tests/test_plugins.py b/netbox/netbox/tests/test_plugins.py index f5f97013e..046436a86 100644 --- a/netbox/netbox/tests/test_plugins.py +++ b/netbox/netbox/tests/test_plugins.py @@ -6,6 +6,7 @@ from django.test import Client, TestCase, override_settings from django.urls import reverse from netbox.tests.dummy_plugin import config as dummy_config +from netbox.tests.dummy_plugin.data_backends import DummyBackend from netbox.plugins.navigation import PluginMenu from netbox.plugins.utils import get_plugin_config from netbox.graphql.schema import Query @@ -111,6 +112,13 @@ class PluginTest(TestCase): """ self.assertIn('netbox.tests.dummy_plugin.middleware.DummyMiddleware', settings.MIDDLEWARE) + def test_data_backends(self): + """ + Check registered data backends. + """ + self.assertIn('dummy', registry['data_backends']) + self.assertIs(registry['data_backends']['dummy'], DummyBackend) + def test_queues(self): """ Check that plugin queues are registered with the accurate name. diff --git a/netbox/netbox/utils.py b/netbox/netbox/utils.py new file mode 100644 index 000000000..f27d1b5f7 --- /dev/null +++ b/netbox/netbox/utils.py @@ -0,0 +1,26 @@ +from netbox.registry import registry + +__all__ = ( + 'get_data_backend_choices', + 'register_data_backend', +) + + +def get_data_backend_choices(): + return [ + (None, '---------'), + *[ + (name, cls.label) for name, cls in registry['data_backends'].items() + ] + ] + + +def register_data_backend(): + """ + Decorator for registering a DataBackend class. + """ + def _wrapper(cls): + registry['data_backends'][cls.name] = cls + return cls + + return _wrapper diff --git a/netbox/templates/core/datasource.html b/netbox/templates/core/datasource.html index 369c395f8..51090b0c9 100644 --- a/netbox/templates/core/datasource.html +++ b/netbox/templates/core/datasource.html @@ -58,7 +58,7 @@ {% trans "URL" %} - {% if not object.is_local %} + {% if not object.type.is_local %} {{ object.source_url }} {% else %} {{ object.source_url }} From c4e765c4a8a5fa4ab653181e8e3e34597b6e1556 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 30 Oct 2023 13:38:03 -0400 Subject: [PATCH 04/10] Closes #14141: translation cleanup (#14143) * Translations cleanup * Tweak variable names; misc string cleanup * Misc cleanup --- netbox/circuits/filtersets.py | 4 +- netbox/circuits/forms/filtersets.py | 2 +- netbox/dcim/choices.py | 8 +- netbox/dcim/forms/common.py | 12 +- .../dcim/models/device_component_templates.py | 8 +- netbox/dcim/models/devices.py | 30 +- netbox/dcim/models/racks.py | 10 +- netbox/extras/models/customfields.py | 8 +- netbox/ipam/forms/model_forms.py | 6 +- netbox/ipam/models/ip.py | 28 +- netbox/ipam/models/vlans.py | 4 +- netbox/netbox/navigation/menu.py | 2 +- netbox/templates/dcim/devicebay_delete.html | 4 +- netbox/translations/en/LC_MESSAGES/django.po | 12322 ++++++++++++++++ netbox/users/forms/model_forms.py | 2 +- netbox/users/models.py | 2 +- netbox/virtualization/forms/model_forms.py | 8 +- netbox/virtualization/models/clusters.py | 9 +- netbox/wireless/models.py | 8 +- 19 files changed, 12408 insertions(+), 69 deletions(-) create mode 100644 netbox/translations/en/LC_MESSAGES/django.po diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py index 5c7168318..4dd726803 100644 --- a/netbox/circuits/filtersets.py +++ b/netbox/circuits/filtersets.py @@ -154,12 +154,12 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte provider_account_id = django_filters.ModelMultipleChoiceFilter( field_name='provider_account', queryset=ProviderAccount.objects.all(), - label=_('ProviderAccount (ID)'), + label=_('Provider account (ID)'), ) provider_network_id = django_filters.ModelMultipleChoiceFilter( field_name='terminations__provider_network', queryset=ProviderNetwork.objects.all(), - label=_('ProviderNetwork (ID)'), + label=_('Provider network (ID)'), ) type_id = django_filters.ModelMultipleChoiceFilter( queryset=CircuitType.objects.all(), diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index 830c10d8b..a82ec1726 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -88,7 +88,7 @@ class ProviderNetworkFilterForm(NetBoxModelFilterSetForm): label=_('Provider') ) service_id = forms.CharField( - label=_('Service id'), + label=_('Service ID'), max_length=100, required=False ) diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index e1d4a330a..2ba24e0aa 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -80,10 +80,10 @@ class RackWidthChoices(ChoiceSet): WIDTH_23IN = 23 CHOICES = ( - (WIDTH_10IN, _('10 inches')), - (WIDTH_19IN, _('19 inches')), - (WIDTH_21IN, _('21 inches')), - (WIDTH_23IN, _('23 inches')), + (WIDTH_10IN, _('{n} inches').format(n=10)), + (WIDTH_19IN, _('{n} inches').format(n=19)), + (WIDTH_21IN, _('{n} inches').format(n=21)), + (WIDTH_23IN, _('{n} inches').format(n=23)), ) diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py index 77543af12..3be4d08e8 100644 --- a/netbox/dcim/forms/common.py +++ b/netbox/dcim/forms/common.py @@ -116,17 +116,17 @@ class ModuleCommonForm(forms.Form): # It is not possible to adopt components already belonging to a module if adopt_components and existing_item and existing_item.module: raise forms.ValidationError( - _("Cannot adopt {name} '{resolved_name}' as it already belongs to a module").format( - name=template.component_model.__name__, - resolved_name=resolved_name + _("Cannot adopt {model} {name} as it already belongs to a module").format( + model=template.component_model.__name__, + name=resolved_name ) ) # If we are not adopting components we error if the component exists if not adopt_components and resolved_name in installed_components: raise forms.ValidationError( - _("{name} - {resolved_name} already exists").format( - name=template.component_model.__name__, - resolved_name=resolved_name + _("A {model} named {name} already exists").format( + model=template.component_model.__name__, + name=resolved_name ) ) diff --git a/netbox/dcim/models/device_component_templates.py b/netbox/dcim/models/device_component_templates.py index 86b6d85fe..5110835f4 100644 --- a/netbox/dcim/models/device_component_templates.py +++ b/netbox/dcim/models/device_component_templates.py @@ -534,14 +534,16 @@ class FrontPortTemplate(ModularComponentTemplateModel): # Validate rear port assignment if self.rear_port.device_type != self.device_type: raise ValidationError( - _("Rear port ({}) must belong to the same device type").format(self.rear_port) + _("Rear port ({name}) must belong to the same device type").format(name=self.rear_port) ) # Validate rear port position assignment if self.rear_port_position > self.rear_port.positions: raise ValidationError( - _("Invalid rear port position ({}); rear port {} has only {} positions").format( - self.rear_port_position, self.rear_port.name, self.rear_port.positions + _("Invalid rear port position ({position}); rear port {name} has only {count} positions").format( + position=self.rear_port_position, + name=self.rear_port.name, + count=self.rear_port.positions ) ) diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 943bf318c..07c1c70f6 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -302,8 +302,10 @@ class DeviceType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): ) if d.position not in u_available: raise ValidationError({ - 'u_height': _("Device {} in rack {} does not have sufficient space to accommodate a height of " - "{}U").format(d, d.rack, self.u_height) + 'u_height': _( + "Device {device} in rack {rack} does not have sufficient space to accommodate a " + "height of {height}U" + ).format(device=d, rack=d.rack, height=self.u_height) }) # If modifying the height of an existing DeviceType to 0U, check for any instances assigned to a rack position. @@ -920,7 +922,7 @@ class Device( if self.primary_ip4: if self.primary_ip4.family != 4: raise ValidationError({ - 'primary_ip4': _("{primary_ip4} is not an IPv4 address.").format(primary_ip4=self.primary_ip4) + 'primary_ip4': _("{ip} is not an IPv4 address.").format(ip=self.primary_ip4) }) if self.primary_ip4.assigned_object in vc_interfaces: pass @@ -929,13 +931,13 @@ class Device( else: raise ValidationError({ 'primary_ip4': _( - "The specified IP address ({primary_ip4}) is not assigned to this device." - ).format(primary_ip4=self.primary_ip4) + "The specified IP address ({ip}) is not assigned to this device." + ).format(ip=self.primary_ip4) }) if self.primary_ip6: if self.primary_ip6.family != 6: raise ValidationError({ - 'primary_ip6': _("{primary_ip6} is not an IPv6 address.").format(primary_ip6=self.primary_ip6m) + 'primary_ip6': _("{ip} is not an IPv6 address.").format(ip=self.primary_ip6) }) if self.primary_ip6.assigned_object in vc_interfaces: pass @@ -944,8 +946,8 @@ class Device( else: raise ValidationError({ 'primary_ip6': _( - "The specified IP address ({primary_ip6}) is not assigned to this device." - ).format(primary_ip6=self.primary_ip6) + "The specified IP address ({ip}) is not assigned to this device." + ).format(ip=self.primary_ip6) }) if self.oob_ip: if self.oob_ip.assigned_object in vc_interfaces: @@ -963,17 +965,19 @@ class Device( raise ValidationError({ 'platform': _( "The assigned platform is limited to {platform_manufacturer} device types, but this device's " - "type belongs to {device_type_manufacturer}." + "type belongs to {devicetype_manufacturer}." ).format( platform_manufacturer=self.platform.manufacturer, - device_type_manufacturer=self.device_type.manufacturer + devicetype_manufacturer=self.device_type.manufacturer ) }) # A Device can only be assigned to a Cluster in the same Site (or no Site) if self.cluster and self.cluster.site is not None and self.cluster.site != self.site: raise ValidationError({ - 'cluster': _("The assigned cluster belongs to a different site ({})").format(self.cluster.site) + 'cluster': _("The assigned cluster belongs to a different site ({site})").format( + site=self.cluster.site + ) }) # Validate virtual chassis assignment @@ -1445,8 +1449,8 @@ class VirtualDeviceContext(PrimaryModel): if primary_ip.family != family: raise ValidationError({ f'primary_ip{family}': _( - "{primary_ip} is not an IPv{family} address." - ).format(family=family, primary_ip=primary_ip) + "{ip} is not an IPv{family} address." + ).format(family=family, ip=primary_ip) }) device_interfaces = self.device.vc_interfaces(if_master=False) if primary_ip.assigned_object not in device_interfaces: diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index ab1027d1b..0d4b844f9 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -562,9 +562,9 @@ class RackReservation(PrimaryModel): invalid_units = [u for u in self.units if u not in self.rack.units] if invalid_units: raise ValidationError({ - 'units': _("Invalid unit(s) for {}U rack: {}").format( - self.rack.u_height, - ', '.join([str(u) for u in invalid_units]), + 'units': _("Invalid unit(s) for {height}U rack: {unit_list}").format( + height=self.rack.u_height, + unit_list=', '.join([str(u) for u in invalid_units]) ), }) @@ -575,8 +575,8 @@ class RackReservation(PrimaryModel): conflicting_units = [u for u in self.units if u in reserved_units] if conflicting_units: raise ValidationError({ - 'units': _('The following units have already been reserved: {}').format( - ', '.join([str(u) for u in conflicting_units]), + 'units': _('The following units have already been reserved: {unit_list}').format( + unit_list=', '.join([str(u) for u in conflicting_units]) ) }) diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 2bed464bb..2cb12ed5b 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -287,8 +287,8 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): except ValidationError as err: raise ValidationError({ 'default': _( - 'Invalid default value "{default}": {message}' - ).format(default=self.default, message=err.message) + 'Invalid default value "{value}": {error}' + ).format(value=self.default, error=err.message) }) # Minimum/maximum values can be set only for numeric fields @@ -332,8 +332,8 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): elif self.object_type: raise ValidationError({ 'object_type': _( - "{type_display} fields may not define an object type.") - .format(type_display=self.get_type_display()) + "{type} fields may not define an object type.") + .format(type=self.get_type_display()) }) def serialize(self, value): diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index bfd4f952d..dd9e6b3e4 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -372,14 +372,14 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): # Do not allow assigning a network ID or broadcast address to an interface. if interface and (address := self.cleaned_data.get('address')): if address.ip == address.network: - msg = _("{address} is a network ID, which may not be assigned to an interface.").format(address=address) + msg = _("{ip} is a network ID, which may not be assigned to an interface.").format(ip=address.ip) if address.version == 4 and address.prefixlen not in (31, 32): raise ValidationError(msg) if address.version == 6 and address.prefixlen not in (127, 128): raise ValidationError(msg) if address.version == 4 and address.ip == address.broadcast and address.prefixlen not in (31, 32): - msg = _("{address} is a broadcast address, which may not be assigned to an interface.").format( - address=address + msg = _("{ip} is a broadcast address, which may not be assigned to an interface.").format( + ip=address.ip ) raise ValidationError(msg) diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index d176d3bff..934cb98c7 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -140,8 +140,11 @@ class Aggregate(GetAvailablePrefixesMixin, PrimaryModel): if covering_aggregates: raise ValidationError({ 'prefix': _( - "Aggregates cannot overlap. {} is already covered by an existing aggregate ({})." - ).format(self.prefix, covering_aggregates[0]) + "Aggregates cannot overlap. {prefix} is already covered by an existing aggregate ({aggregate})." + ).format( + prefix=self.prefix, + aggregate=covering_aggregates[0] + ) }) # Ensure that the aggregate being added does not cover an existing aggregate @@ -150,8 +153,11 @@ class Aggregate(GetAvailablePrefixesMixin, PrimaryModel): covered_aggregates = covered_aggregates.exclude(pk=self.pk) if covered_aggregates: raise ValidationError({ - 'prefix': _("Aggregates cannot overlap. {} covers an existing aggregate ({}).").format( - self.prefix, covered_aggregates[0] + 'prefix': _( + "Prefixes cannot overlap aggregates. {prefix} covers an existing aggregate ({aggregate})." + ).format( + prefix=self.prefix, + aggregate=covered_aggregates[0] ) }) @@ -314,10 +320,11 @@ class Prefix(GetAvailablePrefixesMixin, PrimaryModel): if (self.vrf is None and get_config().ENFORCE_GLOBAL_UNIQUE) or (self.vrf and self.vrf.enforce_unique): duplicate_prefixes = self.get_duplicates() if duplicate_prefixes: + table = _("VRF {vrf}").format(vrf=self.vrf) if self.vrf else _("global table") raise ValidationError({ - 'prefix': _("Duplicate prefix found in {}: {}").format( - _("VRF {}").format(self.vrf) if self.vrf else _("global table"), - duplicate_prefixes.first(), + 'prefix': _("Duplicate prefix found in {table}: {prefix}").format( + table=table, + prefix=duplicate_prefixes.first(), ) }) @@ -843,10 +850,11 @@ class IPAddress(PrimaryModel): self.role not in IPADDRESS_ROLES_NONUNIQUE or any(dip.role not in IPADDRESS_ROLES_NONUNIQUE for dip in duplicate_ips) ): + table = _("VRF {vrf}").format(vrf=self.vrf) if self.vrf else _("global table") raise ValidationError({ - 'address': _("Duplicate IP address found in {}: {}").format( - _("VRF {}").format(self.vrf) if self.vrf else _("global table"), - duplicate_ips.first(), + 'address': _("Duplicate IP address found in {table}: {ipaddress}").format( + table=table, + ipaddress=duplicate_ips.first(), ) }) diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index aa5b36a57..675d03ee5 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -234,8 +234,8 @@ class VLAN(PrimaryModel): if self.group and not self.group.min_vid <= self.vid <= self.group.max_vid: raise ValidationError({ 'vid': _( - "VID must be between {min_vid} and {max_vid} for VLANs in group {group}" - ).format(min_vid=self.group.min_vid, max_vid=self.group.max_vid, group=self.group) + "VID must be between {minimum} and {maximum} for VLANs in group {group}" + ).format(minimum=self.group.min_vid, maximum=self.group.max_vid, group=self.group) }) def get_status_color(self): diff --git a/netbox/netbox/navigation/menu.py b/netbox/netbox/navigation/menu.py index 5b64cfc1e..961fd2035 100644 --- a/netbox/netbox/navigation/menu.py +++ b/netbox/netbox/navigation/menu.py @@ -1,4 +1,4 @@ -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from netbox.registry import registry from utilities.choices import ButtonColorChoices diff --git a/netbox/templates/dcim/devicebay_delete.html b/netbox/templates/dcim/devicebay_delete.html index 18f4f6576..9e54baa86 100644 --- a/netbox/templates/dcim/devicebay_delete.html +++ b/netbox/templates/dcim/devicebay_delete.html @@ -8,8 +8,8 @@ {% block message %}

- {% blocktrans trimmed %} - Are you sure you want to delete this device bay from {{ devicebay.device }}? + {% blocktrans trimmed with device=devicebay.device %} + Are you sure you want to delete this device bay from {{ device }}? {% endblocktrans %}

{% endblock %} diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po new file mode 100644 index 000000000..b04e843f2 --- /dev/null +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -0,0 +1,12322 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-30 17:19+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: account/tables.py:27 templates/account/token.html:23 +#: templates/users/token.html:18 users/forms/bulk_import.py:41 +#: users/forms/model_forms.py:113 +msgid "Key" +msgstr "" + +#: account/tables.py:31 users/forms/filtersets.py:135 +msgid "Write Enabled" +msgstr "" + +#: account/tables.py:34 core/tables/jobs.py:28 extras/choices.py:124 +#: extras/tables/tables.py:469 templates/account/token.html:44 +#: templates/core/job.html:52 templates/extras/configrevision.html:34 +#: templates/extras/configrevision_restore.html:12 +#: templates/extras/htmx/report_result.html:11 +#: templates/extras/htmx/script_result.html:12 +#: templates/extras/journalentry.html:25 templates/generic/object.html:48 +#: templates/users/token.html:36 +msgid "Created" +msgstr "" + +#: account/tables.py:37 templates/account/token.html:48 +#: templates/users/token.html:40 users/forms/bulk_edit.py:97 +#: users/forms/filtersets.py:139 +msgid "Expires" +msgstr "" + +#: account/tables.py:40 users/forms/filtersets.py:144 +msgid "Last Used" +msgstr "" + +#: account/tables.py:43 templates/account/token.html:56 +#: templates/users/token.html:48 users/forms/bulk_edit.py:102 +#: users/forms/model_forms.py:125 +msgid "Allowed IPs" +msgstr "" + +#: circuits/choices.py:21 dcim/choices.py:20 dcim/choices.py:102 +#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1419 +#: dcim/choices.py:1495 dcim/choices.py:1545 virtualization/choices.py:20 +#: virtualization/choices.py:45 +msgid "Planned" +msgstr "" + +#: circuits/choices.py:22 netbox/navigation/menu.py:271 +msgid "Provisioning" +msgstr "" + +#: circuits/choices.py:23 dcim/choices.py:22 dcim/choices.py:103 +#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1494 +#: dcim/choices.py:1544 extras/tables/tables.py:375 ipam/choices.py:31 +#: ipam/choices.py:49 ipam/choices.py:69 ipam/choices.py:154 +#: templates/extras/configcontext.html:26 templates/users/user.html:34 +#: users/forms/bulk_edit.py:36 virtualization/choices.py:22 +#: virtualization/choices.py:44 wireless/choices.py:25 +msgid "Active" +msgstr "" + +#: circuits/choices.py:24 dcim/choices.py:172 dcim/choices.py:218 +#: dcim/choices.py:1493 dcim/choices.py:1546 virtualization/choices.py:24 +#: virtualization/choices.py:43 +msgid "Offline" +msgstr "" + +#: circuits/choices.py:25 +msgid "Deprovisioning" +msgstr "" + +#: circuits/choices.py:26 +msgid "Decommissioned" +msgstr "" + +#: circuits/filtersets.py:29 circuits/filtersets.py:182 dcim/filtersets.py:118 +#: dcim/filtersets.py:179 dcim/filtersets.py:254 dcim/filtersets.py:362 +#: dcim/filtersets.py:873 dcim/filtersets.py:1179 dcim/filtersets.py:1674 +#: dcim/filtersets.py:1847 dcim/filtersets.py:1904 ipam/filtersets.py:304 +#: ipam/filtersets.py:891 ipam/filtersets.py:1122 +#: virtualization/filtersets.py:43 virtualization/filtersets.py:169 +msgid "Region (ID)" +msgstr "" + +#: circuits/filtersets.py:36 circuits/filtersets.py:189 dcim/filtersets.py:124 +#: dcim/filtersets.py:186 dcim/filtersets.py:261 dcim/filtersets.py:369 +#: dcim/filtersets.py:880 dcim/filtersets.py:1186 dcim/filtersets.py:1681 +#: dcim/filtersets.py:1854 dcim/filtersets.py:1911 extras/filtersets.py:383 +#: ipam/filtersets.py:311 ipam/filtersets.py:898 ipam/filtersets.py:1117 +#: virtualization/filtersets.py:50 virtualization/filtersets.py:176 +msgid "Region (slug)" +msgstr "" + +#: circuits/filtersets.py:42 circuits/filtersets.py:195 dcim/filtersets.py:192 +#: dcim/filtersets.py:267 dcim/filtersets.py:375 dcim/filtersets.py:886 +#: dcim/filtersets.py:1192 dcim/filtersets.py:1687 dcim/filtersets.py:1860 +#: dcim/filtersets.py:1917 ipam/filtersets.py:317 ipam/filtersets.py:904 +#: virtualization/filtersets.py:56 virtualization/filtersets.py:182 +msgid "Site group (ID)" +msgstr "" + +#: circuits/filtersets.py:49 circuits/filtersets.py:202 dcim/filtersets.py:199 +#: dcim/filtersets.py:274 dcim/filtersets.py:382 dcim/filtersets.py:893 +#: dcim/filtersets.py:1199 dcim/filtersets.py:1694 dcim/filtersets.py:1867 +#: dcim/filtersets.py:1924 extras/filtersets.py:389 ipam/filtersets.py:324 +#: ipam/filtersets.py:911 virtualization/filtersets.py:63 +#: virtualization/filtersets.py:189 +msgid "Site group (slug)" +msgstr "" + +#: circuits/filtersets.py:54 circuits/forms/bulk_import.py:117 +#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:170 +#: circuits/forms/model_forms.py:137 dcim/forms/bulk_edit.py:166 +#: dcim/forms/bulk_edit.py:238 dcim/forms/bulk_edit.py:570 +#: dcim/forms/bulk_edit.py:763 dcim/forms/bulk_import.py:130 +#: dcim/forms/bulk_import.py:176 dcim/forms/bulk_import.py:249 +#: dcim/forms/bulk_import.py:477 dcim/forms/bulk_import.py:1239 +#: dcim/forms/bulk_import.py:1267 dcim/forms/filtersets.py:83 +#: dcim/forms/filtersets.py:215 dcim/forms/filtersets.py:261 +#: dcim/forms/filtersets.py:370 dcim/forms/filtersets.py:673 +#: dcim/forms/filtersets.py:903 dcim/forms/filtersets.py:927 +#: dcim/forms/filtersets.py:1016 dcim/forms/filtersets.py:1054 +#: dcim/forms/filtersets.py:1459 dcim/forms/filtersets.py:1483 +#: dcim/forms/filtersets.py:1507 dcim/forms/model_forms.py:138 +#: dcim/forms/model_forms.py:167 dcim/forms/model_forms.py:211 +#: dcim/forms/model_forms.py:397 dcim/forms/model_forms.py:629 +#: dcim/forms/object_create.py:357 dcim/tables/devices.py:186 +#: dcim/tables/power.py:26 dcim/tables/racks.py:62 dcim/tables/racks.py:138 +#: dcim/tables/sites.py:129 extras/filtersets.py:399 +#: ipam/forms/bulk_edit.py:217 ipam/forms/bulk_edit.py:271 +#: ipam/forms/bulk_edit.py:449 ipam/forms/bulk_edit.py:521 +#: ipam/forms/bulk_import.py:173 ipam/forms/bulk_import.py:440 +#: ipam/forms/filtersets.py:156 ipam/forms/filtersets.py:230 +#: ipam/forms/filtersets.py:420 ipam/forms/filtersets.py:472 +#: ipam/forms/filtersets.py:585 ipam/forms/model_forms.py:208 +#: ipam/forms/model_forms.py:550 ipam/forms/model_forms.py:642 +#: ipam/tables/ip.py:244 ipam/tables/vlans.py:114 ipam/tables/vlans.py:216 +#: templates/circuits/circuittermination_edit.html:20 +#: templates/circuits/inc/circuit_termination.html:33 +#: templates/dcim/device.html:30 templates/dcim/inc/cable_termination.html:8 +#: templates/dcim/inc/cable_termination.html:33 templates/dcim/location.html:40 +#: templates/dcim/powerpanel.html:23 templates/dcim/rack.html:18 +#: templates/dcim/rackreservation.html:25 templates/dcim/site.html:26 +#: templates/ipam/prefix.html:48 templates/ipam/vlan.html:17 +#: templates/ipam/vlan_edit.html:40 templates/virtualization/cluster.html:45 +#: templates/virtualization/virtualmachine.html:96 +#: virtualization/forms/bulk_edit.py:88 virtualization/forms/bulk_edit.py:97 +#: virtualization/forms/bulk_edit.py:106 virtualization/forms/bulk_edit.py:121 +#: virtualization/forms/bulk_import.py:58 +#: virtualization/forms/bulk_import.py:84 virtualization/forms/filtersets.py:75 +#: virtualization/forms/filtersets.py:141 +#: virtualization/forms/model_forms.py:73 +#: virtualization/forms/model_forms.py:106 +#: virtualization/forms/model_forms.py:173 virtualization/tables/clusters.py:77 +#: virtualization/tables/virtualmachines.py:51 wireless/forms/model_forms.py:77 +#: wireless/forms/model_forms.py:117 +msgid "Site" +msgstr "" + +#: circuits/filtersets.py:60 circuits/filtersets.py:213 +#: circuits/filtersets.py:250 dcim/filtersets.py:209 dcim/filtersets.py:284 +#: dcim/filtersets.py:356 extras/filtersets.py:405 ipam/filtersets.py:215 +#: ipam/filtersets.py:334 ipam/filtersets.py:921 ipam/filtersets.py:1127 +#: virtualization/filtersets.py:73 virtualization/filtersets.py:199 +msgid "Site (slug)" +msgstr "" + +#: circuits/filtersets.py:65 +msgid "ASN (ID)" +msgstr "" + +#: circuits/filtersets.py:86 circuits/filtersets.py:112 +#: circuits/filtersets.py:146 +msgid "Provider (ID)" +msgstr "" + +#: circuits/filtersets.py:92 circuits/filtersets.py:118 +#: circuits/filtersets.py:152 +msgid "Provider (slug)" +msgstr "" + +#: circuits/filtersets.py:157 +msgid "Provider account (ID)" +msgstr "" + +#: circuits/filtersets.py:162 +msgid "Provider network (ID)" +msgstr "" + +#: circuits/filtersets.py:166 +msgid "Circuit type (ID)" +msgstr "" + +#: circuits/filtersets.py:172 +msgid "Circuit type (slug)" +msgstr "" + +#: circuits/filtersets.py:207 circuits/filtersets.py:244 dcim/filtersets.py:203 +#: dcim/filtersets.py:278 dcim/filtersets.py:350 dcim/filtersets.py:897 +#: dcim/filtersets.py:1204 dcim/filtersets.py:1699 dcim/filtersets.py:1871 +#: dcim/filtersets.py:1929 ipam/filtersets.py:209 ipam/filtersets.py:328 +#: ipam/filtersets.py:915 ipam/filtersets.py:1132 +#: virtualization/filtersets.py:67 virtualization/filtersets.py:193 +msgid "Site (ID)" +msgstr "" + +#: circuits/filtersets.py:236 core/filtersets.py:72 dcim/filtersets.py:631 +#: dcim/filtersets.py:1173 dcim/filtersets.py:1975 extras/filtersets.py:40 +#: extras/filtersets.py:69 extras/filtersets.py:108 extras/filtersets.py:137 +#: extras/filtersets.py:164 extras/filtersets.py:195 extras/filtersets.py:264 +#: extras/filtersets.py:312 extras/filtersets.py:372 extras/filtersets.py:531 +#: extras/filtersets.py:573 extras/filtersets.py:614 extras/filtersets.py:637 +#: ipam/forms/model_forms.py:432 netbox/filtersets.py:275 +#: netbox/forms/__init__.py:23 netbox/forms/base.py:151 +#: templates/htmx/object_selector.html:28 templates/inc/filter_list.html:53 +#: templates/ipam/ipaddress_assign.html:32 templates/search.html:7 +#: templates/search.html:26 tenancy/filtersets.py:87 users/filtersets.py:21 +#: users/filtersets.py:37 users/filtersets.py:69 users/filtersets.py:117 +#: utilities/forms/forms.py:99 +msgid "Search" +msgstr "" + +#: circuits/filtersets.py:240 circuits/forms/bulk_edit.py:167 +#: circuits/forms/model_forms.py:110 circuits/forms/model_forms.py:132 +#: dcim/forms/connections.py:66 templates/circuits/circuit.html:15 +#: templates/dcim/inc/cable_termination.html:55 +#: templates/dcim/trace/circuit.html:4 +msgid "Circuit" +msgstr "" + +#: circuits/filtersets.py:254 +msgid "ProviderNetwork (ID)" +msgstr "" + +#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:56 +#: circuits/forms/model_forms.py:26 circuits/tables/providers.py:33 +#: dcim/forms/bulk_edit.py:126 dcim/forms/filtersets.py:185 +#: dcim/forms/model_forms.py:126 dcim/tables/sites.py:94 +#: ipam/models/asns.py:126 ipam/tables/asn.py:27 ipam/views.py:221 +#: netbox/navigation/menu.py:160 netbox/navigation/menu.py:163 +#: templates/circuits/provider.html:24 +msgid "ASNs" +msgstr "" + +#: circuits/forms/bulk_edit.py:29 circuits/forms/bulk_edit.py:51 +#: circuits/forms/bulk_edit.py:78 circuits/forms/bulk_edit.py:99 +#: circuits/forms/bulk_edit.py:159 core/forms/bulk_edit.py:27 +#: dcim/forms/bulk_create.py:35 dcim/forms/bulk_edit.py:71 +#: dcim/forms/bulk_edit.py:90 dcim/forms/bulk_edit.py:149 +#: dcim/forms/bulk_edit.py:190 dcim/forms/bulk_edit.py:208 +#: dcim/forms/bulk_edit.py:336 dcim/forms/bulk_edit.py:371 +#: dcim/forms/bulk_edit.py:386 dcim/forms/bulk_edit.py:445 +#: dcim/forms/bulk_edit.py:484 dcim/forms/bulk_edit.py:514 +#: dcim/forms/bulk_edit.py:538 dcim/forms/bulk_edit.py:608 +#: dcim/forms/bulk_edit.py:657 dcim/forms/bulk_edit.py:709 +#: dcim/forms/bulk_edit.py:732 dcim/forms/bulk_edit.py:780 +#: dcim/forms/bulk_edit.py:850 dcim/forms/bulk_edit.py:903 +#: dcim/forms/bulk_edit.py:938 dcim/forms/bulk_edit.py:978 +#: dcim/forms/bulk_edit.py:1022 dcim/forms/bulk_edit.py:1067 +#: dcim/forms/bulk_edit.py:1094 dcim/forms/bulk_edit.py:1112 +#: dcim/forms/bulk_edit.py:1130 dcim/forms/bulk_edit.py:1148 +#: dcim/forms/bulk_edit.py:1566 extras/forms/bulk_edit.py:35 +#: extras/forms/bulk_edit.py:118 extras/forms/bulk_edit.py:147 +#: extras/forms/bulk_edit.py:242 extras/forms/bulk_edit.py:266 +#: extras/forms/bulk_edit.py:280 extras/tables/tables.py:78 +#: ipam/forms/bulk_edit.py:52 ipam/forms/bulk_edit.py:72 +#: ipam/forms/bulk_edit.py:92 ipam/forms/bulk_edit.py:116 +#: ipam/forms/bulk_edit.py:145 ipam/forms/bulk_edit.py:174 +#: ipam/forms/bulk_edit.py:193 ipam/forms/bulk_edit.py:262 +#: ipam/forms/bulk_edit.py:306 ipam/forms/bulk_edit.py:354 +#: ipam/forms/bulk_edit.py:397 ipam/forms/bulk_edit.py:425 +#: ipam/forms/bulk_edit.py:553 ipam/forms/bulk_edit.py:584 +#: ipam/forms/bulk_edit.py:613 templates/account/token.html:36 +#: templates/circuits/circuit.html:60 templates/circuits/circuittype.html:29 +#: templates/circuits/inc/circuit_termination.html:115 +#: templates/circuits/provider.html:34 +#: templates/circuits/providernetwork.html:35 templates/core/datasource.html:55 +#: templates/dcim/cable.html:37 templates/dcim/consoleport.html:47 +#: templates/dcim/consoleserverport.html:47 templates/dcim/device.html:113 +#: templates/dcim/devicebay.html:35 templates/dcim/devicerole.html:33 +#: templates/dcim/devicetype.html:36 templates/dcim/frontport.html:61 +#: templates/dcim/interface.html:70 templates/dcim/inventoryitem.html:61 +#: templates/dcim/inventoryitemrole.html:23 templates/dcim/location.html:36 +#: templates/dcim/manufacturer.html:43 templates/dcim/module.html:71 +#: templates/dcim/modulebay.html:39 templates/dcim/moduletype.html:27 +#: templates/dcim/platform.html:36 templates/dcim/powerfeed.html:43 +#: templates/dcim/poweroutlet.html:43 templates/dcim/powerpanel.html:31 +#: templates/dcim/powerport.html:43 templates/dcim/rack.html:61 +#: templates/dcim/rackreservation.html:69 templates/dcim/rackrole.html:29 +#: templates/dcim/rearport.html:57 templates/dcim/region.html:34 +#: templates/dcim/site.html:73 templates/dcim/sitegroup.html:34 +#: templates/dcim/virtualchassis.html:32 +#: templates/extras/admin/plugins_list.html:26 +#: templates/extras/configcontext.html:22 +#: templates/extras/configtemplate.html:18 templates/extras/customfield.html:35 +#: templates/extras/dashboard/widget_add.html:14 +#: templates/extras/exporttemplate.html:25 templates/extras/report_list.html:47 +#: templates/extras/savedfilter.html:18 templates/extras/script_list.html:53 +#: templates/extras/tag.html:23 templates/generic/bulk_import.html:118 +#: templates/ipam/aggregate.html:44 templates/ipam/asn.html:43 +#: templates/ipam/asnrange.html:39 templates/ipam/fhrpgroup.html:35 +#: templates/ipam/ipaddress.html:58 templates/ipam/iprange.html:70 +#: templates/ipam/l2vpn.html:27 templates/ipam/prefix.html:82 +#: templates/ipam/rir.html:29 templates/ipam/role.html:29 +#: templates/ipam/routetarget.html:22 templates/ipam/service.html:53 +#: templates/ipam/servicetemplate.html:28 templates/ipam/vlan.html:65 +#: templates/ipam/vlangroup.html:35 templates/ipam/vrf.html:36 +#: templates/tenancy/contact.html:68 templates/tenancy/contactgroup.html:28 +#: templates/tenancy/contactrole.html:23 templates/tenancy/tenant.html:25 +#: templates/tenancy/tenantgroup.html:36 +#: templates/users/objectpermission.html:22 templates/users/token.html:28 +#: templates/virtualization/cluster.html:28 +#: templates/virtualization/clustergroup.html:29 +#: templates/virtualization/clustertype.html:29 +#: templates/virtualization/virtualmachine.html:34 +#: templates/virtualization/vminterface.html:54 +#: templates/wireless/wirelesslan.html:27 +#: templates/wireless/wirelesslangroup.html:34 +#: templates/wireless/wirelesslink.html:37 tenancy/forms/bulk_edit.py:31 +#: tenancy/forms/bulk_edit.py:79 tenancy/forms/bulk_edit.py:121 +#: users/forms/bulk_edit.py:62 users/forms/bulk_edit.py:92 +#: virtualization/forms/bulk_edit.py:29 virtualization/forms/bulk_edit.py:43 +#: virtualization/forms/bulk_edit.py:174 virtualization/forms/bulk_edit.py:225 +#: wireless/forms/bulk_edit.py:28 wireless/forms/bulk_edit.py:81 +#: wireless/forms/bulk_edit.py:128 +msgid "Description" +msgstr "" + +#: circuits/forms/bulk_edit.py:46 circuits/forms/bulk_edit.py:68 +#: circuits/forms/bulk_edit.py:118 circuits/forms/bulk_import.py:35 +#: circuits/forms/bulk_import.py:50 circuits/forms/bulk_import.py:76 +#: circuits/forms/filtersets.py:70 circuits/forms/filtersets.py:88 +#: circuits/forms/filtersets.py:116 circuits/forms/filtersets.py:130 +#: circuits/forms/model_forms.py:32 circuits/forms/model_forms.py:44 +#: circuits/forms/model_forms.py:58 circuits/forms/model_forms.py:92 +#: circuits/tables/circuits.py:55 circuits/tables/providers.py:72 +#: circuits/tables/providers.py:103 templates/circuits/circuit.html:19 +#: templates/circuits/provider.html:20 +#: templates/circuits/provideraccount.html:21 +#: templates/circuits/providernetwork.html:23 +#: templates/dcim/inc/cable_termination.html:51 +msgid "Provider" +msgstr "" + +#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:91 +#: templates/circuits/providernetwork.html:31 +msgid "Service ID" +msgstr "" + +#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:107 +#: dcim/forms/bulk_edit.py:204 dcim/forms/bulk_edit.py:500 +#: dcim/forms/bulk_edit.py:694 dcim/forms/bulk_edit.py:1063 +#: dcim/forms/bulk_edit.py:1090 dcim/forms/bulk_edit.py:1562 +#: dcim/forms/filtersets.py:970 dcim/forms/filtersets.py:1344 +#: dcim/forms/filtersets.py:1365 dcim/tables/devices.py:700 +#: dcim/tables/devices.py:760 dcim/tables/devices.py:983 +#: dcim/tables/devicetypes.py:245 dcim/tables/devicetypes.py:260 +#: dcim/tables/racks.py:32 extras/forms/bulk_edit.py:238 +#: extras/tables/tables.py:323 templates/circuits/circuittype.html:33 +#: templates/dcim/cable.html:41 templates/dcim/devicerole.html:37 +#: templates/dcim/frontport.html:43 templates/dcim/inventoryitemrole.html:27 +#: templates/dcim/rackrole.html:33 templates/dcim/rearport.html:43 +#: templates/extras/tag.html:29 +msgid "Color" +msgstr "" + +#: circuits/forms/bulk_edit.py:113 circuits/forms/bulk_import.py:89 +#: circuits/forms/filtersets.py:125 core/forms/bulk_edit.py:17 +#: core/forms/filtersets.py:30 core/tables/data.py:20 core/tables/jobs.py:18 +#: dcim/forms/bulk_edit.py:281 dcim/forms/bulk_edit.py:672 +#: dcim/forms/bulk_edit.py:811 dcim/forms/bulk_edit.py:879 +#: dcim/forms/bulk_edit.py:898 dcim/forms/bulk_edit.py:921 +#: dcim/forms/bulk_edit.py:963 dcim/forms/bulk_edit.py:1007 +#: dcim/forms/bulk_edit.py:1058 dcim/forms/bulk_edit.py:1085 +#: dcim/forms/bulk_import.py:206 dcim/forms/bulk_import.py:645 +#: dcim/forms/bulk_import.py:671 dcim/forms/bulk_import.py:697 +#: dcim/forms/bulk_import.py:717 dcim/forms/bulk_import.py:800 +#: dcim/forms/bulk_import.py:890 dcim/forms/bulk_import.py:932 +#: dcim/forms/bulk_import.py:1145 dcim/forms/bulk_import.py:1304 +#: dcim/forms/filtersets.py:283 dcim/forms/filtersets.py:860 +#: dcim/forms/filtersets.py:960 dcim/forms/filtersets.py:1080 +#: dcim/forms/filtersets.py:1150 dcim/forms/filtersets.py:1172 +#: dcim/forms/filtersets.py:1194 dcim/forms/filtersets.py:1211 +#: dcim/forms/filtersets.py:1244 dcim/forms/filtersets.py:1339 +#: dcim/forms/filtersets.py:1360 dcim/forms/object_import.py:89 +#: dcim/forms/object_import.py:118 dcim/forms/object_import.py:150 +#: dcim/tables/devices.py:211 dcim/tables/devices.py:816 +#: dcim/tables/power.py:77 extras/forms/bulk_import.py:37 +#: extras/tables/tables.py:345 extras/tables/tables.py:443 +#: ipam/forms/bulk_edit.py:603 ipam/forms/bulk_import.py:524 +#: ipam/forms/filtersets.py:537 netbox/tables/tables.py:225 +#: templates/circuits/circuit.html:31 templates/core/datasource.html:39 +#: templates/dcim/cable.html:16 templates/dcim/consoleport.html:39 +#: templates/dcim/consoleserverport.html:39 templates/dcim/frontport.html:39 +#: templates/dcim/interface.html:47 templates/dcim/interface.html:171 +#: templates/dcim/interface.html:319 templates/dcim/powerfeed.html:35 +#: templates/dcim/poweroutlet.html:39 templates/dcim/powerport.html:39 +#: templates/dcim/rack.html:88 templates/dcim/rearport.html:39 +#: templates/ipam/l2vpn.html:23 templates/virtualization/cluster.html:20 +#: templates/wireless/inc/authentication_attrs.html:9 +#: templates/wireless/inc/wirelesslink_interface.html:14 +#: virtualization/forms/bulk_edit.py:57 virtualization/forms/bulk_import.py:40 +#: virtualization/forms/filtersets.py:50 virtualization/forms/model_forms.py:64 +#: virtualization/tables/clusters.py:66 +msgid "Type" +msgstr "" + +#: circuits/forms/bulk_edit.py:123 circuits/forms/bulk_import.py:82 +#: circuits/forms/filtersets.py:138 circuits/forms/model_forms.py:97 +msgid "Provider account" +msgstr "" + +#: circuits/forms/bulk_edit.py:131 circuits/forms/bulk_import.py:95 +#: circuits/forms/filtersets.py:149 core/forms/filtersets.py:35 +#: core/forms/filtersets.py:76 core/tables/data.py:23 core/tables/jobs.py:25 +#: dcim/forms/bulk_edit.py:104 dcim/forms/bulk_edit.py:179 +#: dcim/forms/bulk_edit.py:260 dcim/forms/bulk_edit.py:593 +#: dcim/forms/bulk_edit.py:646 dcim/forms/bulk_edit.py:678 +#: dcim/forms/bulk_edit.py:805 dcim/forms/bulk_edit.py:1585 +#: dcim/forms/bulk_import.py:87 dcim/forms/bulk_import.py:146 +#: dcim/forms/bulk_import.py:194 dcim/forms/bulk_import.py:442 +#: dcim/forms/bulk_import.py:596 dcim/forms/bulk_import.py:1139 +#: dcim/forms/bulk_import.py:1299 dcim/forms/filtersets.py:168 +#: dcim/forms/filtersets.py:227 dcim/forms/filtersets.py:278 +#: dcim/forms/filtersets.py:719 dcim/forms/filtersets.py:828 +#: dcim/forms/filtersets.py:864 dcim/forms/filtersets.py:965 +#: dcim/forms/filtersets.py:1075 dcim/tables/devices.py:173 +#: dcim/tables/devices.py:819 dcim/tables/devices.py:1043 +#: dcim/tables/modules.py:69 dcim/tables/power.py:74 dcim/tables/racks.py:66 +#: dcim/tables/sites.py:82 dcim/tables/sites.py:133 ipam/forms/bulk_edit.py:242 +#: ipam/forms/bulk_edit.py:291 ipam/forms/bulk_edit.py:339 +#: ipam/forms/bulk_edit.py:543 ipam/forms/bulk_import.py:194 +#: ipam/forms/bulk_import.py:259 ipam/forms/bulk_import.py:295 +#: ipam/forms/bulk_import.py:461 ipam/forms/filtersets.py:209 +#: ipam/forms/filtersets.py:274 ipam/forms/filtersets.py:344 +#: ipam/forms/filtersets.py:484 ipam/forms/model_forms.py:451 +#: ipam/tables/ip.py:236 ipam/tables/ip.py:309 ipam/tables/ip.py:359 +#: ipam/tables/ip.py:421 ipam/tables/ip.py:448 ipam/tables/vlans.py:122 +#: ipam/tables/vlans.py:227 templates/circuits/circuit.html:35 +#: templates/core/datasource.html:47 templates/core/job.html:35 +#: templates/dcim/cable.html:20 templates/dcim/device.html:200 +#: templates/dcim/location.html:48 templates/dcim/module.html:67 +#: templates/dcim/powerfeed.html:39 templates/dcim/rack.html:53 +#: templates/dcim/site.html:56 templates/extras/report_list.html:49 +#: templates/extras/script_list.html:55 templates/ipam/ipaddress.html:40 +#: templates/ipam/iprange.html:57 templates/ipam/prefix.html:74 +#: templates/ipam/vlan.html:51 templates/virtualization/cluster.html:24 +#: templates/virtualization/virtualmachine.html:22 +#: templates/wireless/wirelesslan.html:23 +#: templates/wireless/wirelesslink.html:20 users/forms/filtersets.py:35 +#: users/forms/model_forms.py:196 virtualization/forms/bulk_edit.py:67 +#: virtualization/forms/bulk_edit.py:115 virtualization/forms/bulk_import.py:53 +#: virtualization/forms/bulk_import.py:79 virtualization/forms/filtersets.py:58 +#: virtualization/forms/filtersets.py:153 virtualization/tables/clusters.py:74 +#: virtualization/tables/virtualmachines.py:48 wireless/forms/bulk_edit.py:42 +#: wireless/forms/bulk_edit.py:104 wireless/forms/bulk_import.py:43 +#: wireless/forms/bulk_import.py:84 wireless/forms/filtersets.py:48 +#: wireless/forms/filtersets.py:82 wireless/tables/wirelesslan.py:52 +#: wireless/tables/wirelesslink.py:19 +msgid "Status" +msgstr "" + +#: circuits/forms/bulk_edit.py:137 circuits/forms/bulk_import.py:100 +#: circuits/forms/filtersets.py:119 dcim/forms/bulk_edit.py:120 +#: dcim/forms/bulk_edit.py:185 dcim/forms/bulk_edit.py:255 +#: dcim/forms/bulk_edit.py:366 dcim/forms/bulk_edit.py:583 +#: dcim/forms/bulk_edit.py:684 dcim/forms/bulk_edit.py:1590 +#: dcim/forms/bulk_import.py:106 dcim/forms/bulk_import.py:151 +#: dcim/forms/bulk_import.py:187 dcim/forms/bulk_import.py:274 +#: dcim/forms/bulk_import.py:416 dcim/forms/bulk_import.py:1151 +#: dcim/forms/bulk_import.py:1356 dcim/forms/filtersets.py:164 +#: dcim/forms/filtersets.py:195 dcim/forms/filtersets.py:246 +#: dcim/forms/filtersets.py:330 dcim/forms/filtersets.py:351 +#: dcim/forms/filtersets.py:647 dcim/forms/filtersets.py:819 +#: dcim/forms/filtersets.py:884 dcim/forms/filtersets.py:914 +#: dcim/forms/filtersets.py:1035 dcim/tables/power.py:88 +#: extras/filtersets.py:486 extras/forms/filtersets.py:306 +#: extras/forms/filtersets.py:380 ipam/forms/bulk_edit.py:42 +#: ipam/forms/bulk_edit.py:67 ipam/forms/bulk_edit.py:111 +#: ipam/forms/bulk_edit.py:140 ipam/forms/bulk_edit.py:165 +#: ipam/forms/bulk_edit.py:237 ipam/forms/bulk_edit.py:286 +#: ipam/forms/bulk_edit.py:334 ipam/forms/bulk_edit.py:538 +#: ipam/forms/bulk_edit.py:608 ipam/forms/bulk_import.py:40 +#: ipam/forms/bulk_import.py:69 ipam/forms/bulk_import.py:97 +#: ipam/forms/bulk_import.py:117 ipam/forms/bulk_import.py:137 +#: ipam/forms/bulk_import.py:166 ipam/forms/bulk_import.py:252 +#: ipam/forms/bulk_import.py:288 ipam/forms/bulk_import.py:454 +#: ipam/forms/bulk_import.py:518 ipam/forms/filtersets.py:51 +#: ipam/forms/filtersets.py:71 ipam/forms/filtersets.py:103 +#: ipam/forms/filtersets.py:123 ipam/forms/filtersets.py:146 +#: ipam/forms/filtersets.py:173 ipam/forms/filtersets.py:260 +#: ipam/forms/filtersets.py:300 ipam/forms/filtersets.py:453 +#: ipam/forms/filtersets.py:534 ipam/tables/ip.py:451 ipam/tables/vlans.py:224 +#: templates/circuits/circuit.html:39 templates/dcim/cable.html:24 +#: templates/dcim/device.html:98 templates/dcim/location.html:52 +#: templates/dcim/powerfeed.html:47 templates/dcim/rack.html:44 +#: templates/dcim/rackreservation.html:56 templates/dcim/site.html:60 +#: templates/dcim/virtualdevicecontext.html:55 templates/ipam/aggregate.html:31 +#: templates/ipam/asn.html:34 templates/ipam/asnrange.html:30 +#: templates/ipam/ipaddress.html:31 templates/ipam/iprange.html:61 +#: templates/ipam/l2vpn.html:31 templates/ipam/prefix.html:29 +#: templates/ipam/routetarget.html:18 templates/ipam/vlan.html:42 +#: templates/ipam/vrf.html:23 templates/tenancy/tenant.html:17 +#: templates/virtualization/cluster.html:36 +#: templates/virtualization/virtualmachine.html:38 +#: templates/wireless/wirelesslan.html:35 +#: templates/wireless/wirelesslink.html:28 tenancy/forms/forms.py:25 +#: tenancy/forms/forms.py:48 tenancy/forms/model_forms.py:56 +#: tenancy/tables/columns.py:64 virtualization/forms/bulk_edit.py:73 +#: virtualization/forms/bulk_edit.py:152 virtualization/forms/bulk_import.py:65 +#: virtualization/forms/bulk_import.py:114 +#: virtualization/forms/filtersets.py:44 virtualization/forms/filtersets.py:98 +#: wireless/forms/bulk_edit.py:62 wireless/forms/bulk_edit.py:109 +#: wireless/forms/bulk_import.py:55 wireless/forms/bulk_import.py:97 +#: wireless/forms/filtersets.py:34 wireless/forms/filtersets.py:74 +msgid "Tenant" +msgstr "" + +#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:173 +msgid "Install date" +msgstr "" + +#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:178 +msgid "Termination date" +msgstr "" + +#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:185 +msgid "Commit rate (Kbps)" +msgstr "" + +#: circuits/forms/bulk_edit.py:168 circuits/forms/model_forms.py:111 +msgid "Service Parameters" +msgstr "" + +#: circuits/forms/bulk_edit.py:169 circuits/forms/model_forms.py:112 +#: dcim/forms/model_forms.py:141 dcim/forms/model_forms.py:183 +#: dcim/forms/model_forms.py:260 dcim/forms/model_forms.py:671 +#: dcim/forms/model_forms.py:1477 ipam/forms/model_forms.py:63 +#: ipam/forms/model_forms.py:116 ipam/forms/model_forms.py:137 +#: ipam/forms/model_forms.py:161 ipam/forms/model_forms.py:233 +#: ipam/forms/model_forms.py:259 ipam/forms/model_forms.py:781 +#: netbox/navigation/menu.py:38 templates/dcim/cable_edit.html:68 +#: templates/dcim/device_edit.html:85 templates/dcim/rack_edit.html:30 +#: templates/ipam/ipaddress_bulk_add.html:27 +#: templates/ipam/ipaddress_edit.html:27 templates/ipam/vlan_edit.html:22 +#: virtualization/forms/model_forms.py:82 +#: virtualization/forms/model_forms.py:223 wireless/forms/model_forms.py:55 +#: wireless/forms/model_forms.py:160 +msgid "Tenancy" +msgstr "" + +#: circuits/forms/bulk_import.py:38 circuits/forms/bulk_import.py:53 +#: circuits/forms/bulk_import.py:79 +msgid "Assigned provider" +msgstr "" + +#: circuits/forms/bulk_import.py:70 dcim/forms/bulk_import.py:170 +#: dcim/forms/bulk_import.py:380 dcim/forms/bulk_import.py:1092 +#: dcim/forms/bulk_import.py:1171 extras/forms/bulk_import.py:167 +msgid "RGB color in hexadecimal. Example:" +msgstr "" + +#: circuits/forms/bulk_import.py:85 +msgid "Assigned provider account" +msgstr "" + +#: circuits/forms/bulk_import.py:92 +msgid "Type of circuit" +msgstr "" + +#: circuits/forms/bulk_import.py:97 dcim/forms/bulk_import.py:89 +#: dcim/forms/bulk_import.py:148 dcim/forms/bulk_import.py:196 +#: dcim/forms/bulk_import.py:444 dcim/forms/bulk_import.py:598 +#: dcim/forms/bulk_import.py:1301 ipam/forms/bulk_import.py:196 +#: ipam/forms/bulk_import.py:261 ipam/forms/bulk_import.py:297 +#: ipam/forms/bulk_import.py:463 virtualization/forms/bulk_import.py:55 +#: virtualization/forms/bulk_import.py:81 +msgid "Operational status" +msgstr "" + +#: circuits/forms/bulk_import.py:104 dcim/forms/bulk_import.py:110 +#: dcim/forms/bulk_import.py:155 dcim/forms/bulk_import.py:278 +#: dcim/forms/bulk_import.py:420 dcim/forms/bulk_import.py:1155 +#: dcim/forms/bulk_import.py:1296 ipam/forms/bulk_import.py:44 +#: ipam/forms/bulk_import.py:73 ipam/forms/bulk_import.py:101 +#: ipam/forms/bulk_import.py:121 ipam/forms/bulk_import.py:141 +#: ipam/forms/bulk_import.py:170 ipam/forms/bulk_import.py:256 +#: ipam/forms/bulk_import.py:292 ipam/forms/bulk_import.py:458 +#: virtualization/forms/bulk_import.py:69 +#: virtualization/forms/bulk_import.py:118 wireless/forms/bulk_import.py:59 +#: wireless/forms/bulk_import.py:101 +msgid "Assigned tenant" +msgstr "" + +#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:146 +#: circuits/forms/model_forms.py:143 +msgid "Provider network" +msgstr "" + +#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:118 +#: dcim/forms/bulk_edit.py:247 dcim/forms/bulk_edit.py:345 +#: dcim/forms/bulk_edit.py:575 dcim/forms/bulk_edit.py:622 +#: dcim/forms/bulk_edit.py:772 dcim/forms/bulk_import.py:181 +#: dcim/forms/bulk_import.py:255 dcim/forms/bulk_import.py:483 +#: dcim/forms/bulk_import.py:1245 dcim/forms/bulk_import.py:1279 +#: dcim/forms/filtersets.py:91 dcim/forms/filtersets.py:243 +#: dcim/forms/filtersets.py:275 dcim/forms/filtersets.py:327 +#: dcim/forms/filtersets.py:378 dcim/forms/filtersets.py:644 +#: dcim/forms/filtersets.py:682 dcim/forms/filtersets.py:883 +#: dcim/forms/filtersets.py:912 dcim/forms/filtersets.py:932 +#: dcim/forms/filtersets.py:996 dcim/forms/filtersets.py:1025 +#: dcim/forms/filtersets.py:1034 dcim/forms/filtersets.py:1145 +#: dcim/forms/filtersets.py:1167 dcim/forms/filtersets.py:1189 +#: dcim/forms/filtersets.py:1206 dcim/forms/filtersets.py:1226 +#: dcim/forms/filtersets.py:1333 dcim/forms/filtersets.py:1355 +#: dcim/forms/filtersets.py:1376 dcim/forms/filtersets.py:1391 +#: dcim/forms/filtersets.py:1402 dcim/forms/model_forms.py:182 +#: dcim/forms/model_forms.py:216 dcim/forms/model_forms.py:402 +#: dcim/forms/model_forms.py:634 dcim/tables/devices.py:190 +#: dcim/tables/power.py:30 dcim/tables/racks.py:58 dcim/tables/racks.py:143 +#: extras/filtersets.py:410 extras/forms/filtersets.py:303 +#: ipam/forms/bulk_edit.py:458 ipam/forms/filtersets.py:172 +#: ipam/forms/filtersets.py:403 ipam/forms/filtersets.py:425 +#: ipam/forms/filtersets.py:451 ipam/forms/model_forms.py:562 +#: templates/dcim/device.html:34 templates/dcim/device_edit.html:30 +#: templates/dcim/inc/cable_termination.html:12 templates/dcim/location.html:27 +#: templates/dcim/powerpanel.html:27 templates/dcim/rack.html:27 +#: templates/dcim/rackreservation.html:34 virtualization/forms/filtersets.py:43 +#: virtualization/forms/filtersets.py:96 wireless/forms/model_forms.py:88 +#: wireless/forms/model_forms.py:128 +msgid "Location" +msgstr "" + +#: circuits/forms/filtersets.py:27 ipam/forms/model_forms.py:160 +#: ipam/models/asns.py:108 ipam/models/asns.py:125 ipam/tables/asn.py:41 +#: templates/ipam/asn.html:20 +msgid "ASN" +msgstr "" + +#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:120 +#: dcim/forms/filtersets.py:135 dcim/forms/filtersets.py:149 +#: dcim/forms/filtersets.py:165 dcim/forms/filtersets.py:196 +#: dcim/forms/filtersets.py:247 dcim/forms/filtersets.py:331 +#: dcim/forms/filtersets.py:405 dcim/forms/filtersets.py:648 +#: dcim/forms/filtersets.py:997 netbox/navigation/menu.py:45 +#: netbox/navigation/menu.py:47 tenancy/tables/columns.py:70 +#: tenancy/tables/contacts.py:25 tenancy/views.py:23 +#: virtualization/forms/filtersets.py:34 virtualization/forms/filtersets.py:45 +#: virtualization/forms/filtersets.py:99 +msgid "Contacts" +msgstr "" + +#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:156 +#: dcim/forms/bulk_edit.py:110 dcim/forms/bulk_edit.py:222 +#: dcim/forms/bulk_edit.py:747 dcim/forms/bulk_import.py:92 +#: dcim/forms/filtersets.py:69 dcim/forms/filtersets.py:175 +#: dcim/forms/filtersets.py:201 dcim/forms/filtersets.py:253 +#: dcim/forms/filtersets.py:356 dcim/forms/filtersets.py:659 +#: dcim/forms/filtersets.py:889 dcim/forms/filtersets.py:919 +#: dcim/forms/filtersets.py:1002 dcim/forms/filtersets.py:1041 +#: dcim/forms/filtersets.py:1451 dcim/forms/filtersets.py:1475 +#: dcim/forms/filtersets.py:1499 dcim/forms/model_forms.py:80 +#: dcim/forms/model_forms.py:115 dcim/forms/object_create.py:341 +#: dcim/tables/devices.py:176 dcim/tables/sites.py:85 extras/filtersets.py:377 +#: ipam/forms/bulk_edit.py:207 ipam/forms/bulk_edit.py:439 +#: ipam/forms/bulk_edit.py:511 ipam/forms/filtersets.py:216 +#: ipam/forms/filtersets.py:410 ipam/forms/filtersets.py:458 +#: ipam/forms/filtersets.py:576 ipam/forms/model_forms.py:534 +#: templates/dcim/device.html:17 templates/dcim/region.html:26 +#: templates/dcim/site.html:30 virtualization/forms/bulk_edit.py:78 +#: virtualization/forms/filtersets.py:55 virtualization/forms/filtersets.py:126 +#: virtualization/forms/model_forms.py:94 +msgid "Region" +msgstr "" + +#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:161 +#: dcim/forms/bulk_edit.py:230 dcim/forms/bulk_edit.py:755 +#: dcim/forms/filtersets.py:74 dcim/forms/filtersets.py:180 +#: dcim/forms/filtersets.py:206 dcim/forms/filtersets.py:266 +#: dcim/forms/filtersets.py:361 dcim/forms/filtersets.py:664 +#: dcim/forms/filtersets.py:894 dcim/forms/filtersets.py:1007 +#: dcim/forms/filtersets.py:1046 dcim/forms/object_create.py:349 +#: extras/filtersets.py:394 ipam/forms/bulk_edit.py:212 +#: ipam/forms/bulk_edit.py:446 ipam/forms/bulk_edit.py:516 +#: ipam/forms/filtersets.py:221 ipam/forms/filtersets.py:415 +#: ipam/forms/filtersets.py:463 ipam/forms/model_forms.py:547 +#: virtualization/forms/bulk_edit.py:83 virtualization/forms/filtersets.py:65 +#: virtualization/forms/filtersets.py:131 +#: virtualization/forms/model_forms.py:100 +msgid "Site group" +msgstr "" + +#: circuits/forms/filtersets.py:51 +msgid "ASN (legacy)" +msgstr "" + +#: circuits/forms/filtersets.py:65 circuits/forms/filtersets.py:83 +#: circuits/forms/filtersets.py:102 circuits/forms/filtersets.py:117 +#: core/forms/filtersets.py:64 dcim/forms/bulk_edit.py:718 +#: dcim/forms/filtersets.py:163 dcim/forms/filtersets.py:194 +#: dcim/forms/filtersets.py:818 dcim/forms/filtersets.py:913 +#: dcim/forms/filtersets.py:1036 dcim/forms/filtersets.py:1144 +#: dcim/forms/filtersets.py:1166 dcim/forms/filtersets.py:1188 +#: dcim/forms/filtersets.py:1205 dcim/forms/filtersets.py:1222 +#: dcim/forms/filtersets.py:1332 dcim/forms/filtersets.py:1354 +#: dcim/forms/filtersets.py:1375 dcim/forms/filtersets.py:1390 +#: dcim/forms/filtersets.py:1401 extras/forms/filtersets.py:42 +#: extras/forms/filtersets.py:108 extras/forms/filtersets.py:139 +#: extras/forms/filtersets.py:179 extras/forms/filtersets.py:195 +#: extras/forms/filtersets.py:228 extras/forms/filtersets.py:425 +#: extras/forms/filtersets.py:466 ipam/forms/filtersets.py:102 +#: ipam/forms/filtersets.py:259 ipam/forms/filtersets.py:298 +#: ipam/forms/filtersets.py:371 ipam/forms/filtersets.py:452 +#: ipam/forms/filtersets.py:510 ipam/forms/filtersets.py:533 +#: virtualization/forms/filtersets.py:42 virtualization/forms/filtersets.py:97 +#: virtualization/forms/filtersets.py:187 wireless/forms/filtersets.py:33 +#: wireless/forms/filtersets.py:73 +msgid "Attributes" +msgstr "" + +#: circuits/forms/filtersets.py:73 circuits/tables/circuits.py:60 +#: circuits/tables/providers.py:66 templates/circuits/circuit.html:23 +#: templates/circuits/provideraccount.html:25 +msgid "Account" +msgstr "" + +#: circuits/forms/model_forms.py:64 +#: templates/circuits/circuittermination_edit.html:23 +#: templates/circuits/inc/circuit_termination.html:89 +#: templates/circuits/providernetwork.html:18 +msgid "Provider Network" +msgstr "" + +#: circuits/forms/model_forms.py:78 templates/circuits/circuittype.html:20 +msgid "Circuit Type" +msgstr "" + +#: circuits/models/circuits.py:25 dcim/models/cables.py:68 +#: dcim/models/device_component_templates.py:492 +#: dcim/models/device_component_templates.py:592 +#: dcim/models/device_components.py:967 dcim/models/device_components.py:1041 +#: dcim/models/device_components.py:1157 dcim/models/devices.py:467 +#: dcim/models/racks.py:43 extras/models/tags.py:31 +msgid "color" +msgstr "" + +#: circuits/models/circuits.py:34 +msgid "circuit type" +msgstr "" + +#: circuits/models/circuits.py:35 +msgid "circuit types" +msgstr "" + +#: circuits/models/circuits.py:46 +msgid "circuit ID" +msgstr "" + +#: circuits/models/circuits.py:47 +msgid "Unique circuit ID" +msgstr "" + +#: circuits/models/circuits.py:67 core/models/data.py:55 core/models/jobs.py:85 +#: dcim/models/cables.py:50 dcim/models/devices.py:641 +#: dcim/models/devices.py:1160 dcim/models/devices.py:1369 +#: dcim/models/power.py:95 dcim/models/racks.py:97 dcim/models/sites.py:154 +#: dcim/models/sites.py:266 ipam/models/ip.py:252 ipam/models/ip.py:521 +#: ipam/models/ip.py:729 ipam/models/vlans.py:173 +#: virtualization/models/clusters.py:74 +#: virtualization/models/virtualmachines.py:81 wireless/models.py:94 +#: wireless/models.py:158 +msgid "status" +msgstr "" + +#: circuits/models/circuits.py:82 +msgid "installed" +msgstr "" + +#: circuits/models/circuits.py:87 +msgid "terminates" +msgstr "" + +#: circuits/models/circuits.py:92 +msgid "commit rate (Kbps)" +msgstr "" + +#: circuits/models/circuits.py:93 +msgid "Committed rate" +msgstr "" + +#: circuits/models/circuits.py:135 +msgid "circuit" +msgstr "" + +#: circuits/models/circuits.py:136 +msgid "circuits" +msgstr "" + +#: circuits/models/circuits.py:169 +msgid "termination" +msgstr "" + +#: circuits/models/circuits.py:186 +msgid "port speed (Kbps)" +msgstr "" + +#: circuits/models/circuits.py:189 +msgid "Physical circuit speed" +msgstr "" + +#: circuits/models/circuits.py:194 +msgid "upstream speed (Kbps)" +msgstr "" + +#: circuits/models/circuits.py:195 +msgid "Upstream speed, if different from port speed" +msgstr "" + +#: circuits/models/circuits.py:200 +msgid "cross-connect ID" +msgstr "" + +#: circuits/models/circuits.py:201 +msgid "ID of the local cross-connect" +msgstr "" + +#: circuits/models/circuits.py:206 +msgid "patch panel/port(s)" +msgstr "" + +#: circuits/models/circuits.py:207 +msgid "Patch panel ID and port number(s)" +msgstr "" + +#: circuits/models/circuits.py:210 dcim/models/device_component_templates.py:62 +#: dcim/models/device_components.py:70 dcim/models/racks.py:536 +#: extras/models/configs.py:45 extras/models/configs.py:219 +#: extras/models/customfields.py:116 extras/models/models.py:343 +#: extras/models/models.py:458 extras/models/staging.py:31 +#: extras/models/tags.py:35 netbox/models/__init__.py:109 +#: netbox/models/__init__.py:144 netbox/models/__init__.py:190 +#: users/models.py:270 users/models.py:345 +#: virtualization/models/virtualmachines.py:256 +msgid "description" +msgstr "" + +#: circuits/models/circuits.py:223 +msgid "circuit termination" +msgstr "" + +#: circuits/models/circuits.py:224 +msgid "circuit terminations" +msgstr "" + +#: circuits/models/providers.py:22 circuits/models/providers.py:66 +#: circuits/models/providers.py:104 core/models/data.py:42 +#: core/models/jobs.py:46 dcim/models/device_component_templates.py:44 +#: dcim/models/device_components.py:55 dcim/models/devices.py:581 +#: dcim/models/devices.py:1300 dcim/models/devices.py:1365 +#: dcim/models/power.py:39 dcim/models/power.py:91 dcim/models/racks.py:62 +#: dcim/models/sites.py:138 extras/models/configs.py:36 +#: extras/models/configs.py:215 extras/models/customfields.py:83 +#: extras/models/models.py:55 extras/models/models.py:243 +#: extras/models/models.py:339 extras/models/models.py:448 +#: extras/models/models.py:543 extras/models/staging.py:26 +#: ipam/models/asns.py:18 ipam/models/fhrp.py:26 ipam/models/l2vpn.py:22 +#: ipam/models/services.py:52 ipam/models/services.py:88 +#: ipam/models/vlans.py:27 ipam/models/vlans.py:162 ipam/models/vrfs.py:22 +#: ipam/models/vrfs.py:79 netbox/models/__init__.py:136 +#: netbox/models/__init__.py:180 tenancy/models/contacts.py:63 +#: tenancy/models/tenants.py:20 tenancy/models/tenants.py:45 +#: users/models.py:341 virtualization/models/clusters.py:57 +#: virtualization/models/virtualmachines.py:69 +#: virtualization/models/virtualmachines.py:246 wireless/models.py:50 +msgid "name" +msgstr "" + +#: circuits/models/providers.py:25 +msgid "Full name of the provider" +msgstr "" + +#: circuits/models/providers.py:28 dcim/models/devices.py:86 +#: dcim/models/sites.py:149 extras/models/models.py:453 ipam/models/asns.py:23 +#: ipam/models/l2vpn.py:27 ipam/models/vlans.py:31 +#: netbox/models/__init__.py:140 netbox/models/__init__.py:185 +#: tenancy/models/tenants.py:25 tenancy/models/tenants.py:49 +#: wireless/models.py:55 +msgid "slug" +msgstr "" + +#: circuits/models/providers.py:42 +msgid "provider" +msgstr "" + +#: circuits/models/providers.py:43 +msgid "providers" +msgstr "" + +#: circuits/models/providers.py:63 +msgid "account ID" +msgstr "" + +#: circuits/models/providers.py:86 +msgid "provider account" +msgstr "" + +#: circuits/models/providers.py:87 +msgid "provider accounts" +msgstr "" + +#: circuits/models/providers.py:115 +msgid "service ID" +msgstr "" + +#: circuits/models/providers.py:126 +msgid "provider network" +msgstr "" + +#: circuits/models/providers.py:127 +msgid "provider networks" +msgstr "" + +#: circuits/tables/circuits.py:29 circuits/tables/providers.py:18 +#: circuits/tables/providers.py:69 circuits/tables/providers.py:99 +#: core/tables/data.py:16 core/tables/jobs.py:14 dcim/forms/filtersets.py:59 +#: dcim/forms/object_create.py:42 dcim/tables/devices.py:88 +#: dcim/tables/devices.py:125 dcim/tables/devices.py:167 +#: dcim/tables/devices.py:318 dcim/tables/devices.py:395 +#: dcim/tables/devices.py:439 dcim/tables/devices.py:485 +#: dcim/tables/devices.py:537 dcim/tables/devices.py:646 +#: dcim/tables/devices.py:727 dcim/tables/devices.py:777 +#: dcim/tables/devices.py:843 dcim/tables/devices.py:954 +#: dcim/tables/devices.py:974 dcim/tables/devices.py:1003 +#: dcim/tables/devices.py:1033 dcim/tables/devicetypes.py:32 +#: dcim/tables/power.py:22 dcim/tables/power.py:62 dcim/tables/racks.py:23 +#: dcim/tables/racks.py:53 dcim/tables/sites.py:24 dcim/tables/sites.py:51 +#: dcim/tables/sites.py:78 dcim/tables/sites.py:125 +#: extras/forms/filtersets.py:187 extras/tables/tables.py:65 +#: extras/tables/tables.py:105 extras/tables/tables.py:137 +#: extras/tables/tables.py:161 extras/tables/tables.py:226 +#: extras/tables/tables.py:273 extras/tables/tables.py:319 +#: extras/tables/tables.py:371 extras/tables/tables.py:394 +#: ipam/forms/bulk_edit.py:392 ipam/forms/filtersets.py:375 +#: ipam/tables/asn.py:16 ipam/tables/ip.py:85 ipam/tables/ip.py:159 +#: ipam/tables/l2vpn.py:23 ipam/tables/services.py:15 +#: ipam/tables/services.py:40 ipam/tables/vlans.py:64 ipam/tables/vlans.py:110 +#: ipam/tables/vrfs.py:26 ipam/tables/vrfs.py:67 +#: templates/circuits/circuittype.html:25 +#: templates/circuits/provideraccount.html:29 +#: templates/circuits/providernetwork.html:27 templates/core/datasource.html:35 +#: templates/core/job.html:31 templates/dcim/consoleport.html:31 +#: templates/dcim/consoleserverport.html:31 templates/dcim/devicebay.html:27 +#: templates/dcim/devicerole.html:29 templates/dcim/frontport.html:31 +#: templates/dcim/inc/interface_vlans_table.html:5 +#: templates/dcim/inc/panels/inventory_items.html:10 +#: templates/dcim/interface.html:39 templates/dcim/interface.html:167 +#: templates/dcim/inventoryitem.html:29 +#: templates/dcim/inventoryitemrole.html:19 templates/dcim/location.html:32 +#: templates/dcim/manufacturer.html:39 templates/dcim/modulebay.html:27 +#: templates/dcim/platform.html:32 templates/dcim/poweroutlet.html:31 +#: templates/dcim/powerport.html:31 templates/dcim/rackrole.html:25 +#: templates/dcim/rearport.html:31 templates/dcim/region.html:30 +#: templates/dcim/sitegroup.html:30 templates/dcim/virtualdevicecontext.html:21 +#: templates/extras/admin/plugins_list.html:22 +#: templates/extras/configcontext.html:14 +#: templates/extras/configtemplate.html:14 templates/extras/customfield.html:16 +#: templates/extras/customlink.html:14 templates/extras/exporttemplate.html:21 +#: templates/extras/report_list.html:46 templates/extras/savedfilter.html:14 +#: templates/extras/script_list.html:52 templates/extras/tag.html:17 +#: templates/extras/webhook.html:16 templates/ipam/asnrange.html:16 +#: templates/ipam/fhrpgroup.html:31 templates/ipam/l2vpn.html:15 +#: templates/ipam/rir.html:25 templates/ipam/role.html:25 +#: templates/ipam/routetarget.html:14 templates/ipam/service.html:27 +#: templates/ipam/servicetemplate.html:16 templates/ipam/vlan.html:38 +#: templates/ipam/vlangroup.html:31 templates/tenancy/contact.html:26 +#: templates/tenancy/contactgroup.html:24 templates/tenancy/contactrole.html:19 +#: templates/tenancy/tenantgroup.html:32 templates/users/group.html:18 +#: templates/users/objectpermission.html:18 +#: templates/virtualization/cluster.html:16 +#: templates/virtualization/clustergroup.html:25 +#: templates/virtualization/clustertype.html:25 +#: templates/virtualization/virtualmachine.html:18 +#: templates/virtualization/vminterface.html:28 +#: templates/wireless/wirelesslangroup.html:30 tenancy/tables/contacts.py:19 +#: tenancy/tables/contacts.py:41 tenancy/tables/contacts.py:56 +#: tenancy/tables/tenants.py:16 tenancy/tables/tenants.py:38 users/tables.py:62 +#: users/tables.py:79 virtualization/forms/bulk_create.py:19 +#: virtualization/forms/object_create.py:12 +#: virtualization/tables/clusters.py:17 virtualization/tables/clusters.py:39 +#: virtualization/tables/clusters.py:62 +#: virtualization/tables/virtualmachines.py:43 +#: virtualization/tables/virtualmachines.py:114 +#: wireless/tables/wirelesslan.py:18 wireless/tables/wirelesslan.py:79 +msgid "Name" +msgstr "" + +#: circuits/tables/circuits.py:38 circuits/tables/providers.py:45 +#: circuits/tables/providers.py:79 netbox/navigation/menu.py:235 +#: netbox/navigation/menu.py:239 netbox/navigation/menu.py:241 +#: templates/circuits/provider.html:61 +#: templates/circuits/provideraccount.html:46 +#: templates/circuits/providernetwork.html:54 +msgid "Circuits" +msgstr "" + +#: circuits/tables/circuits.py:52 templates/circuits/circuit.html:27 +msgid "Circuit ID" +msgstr "" + +#: circuits/tables/circuits.py:65 wireless/forms/model_forms.py:157 +msgid "Side A" +msgstr "" + +#: circuits/tables/circuits.py:69 +msgid "Side Z" +msgstr "" + +#: circuits/tables/circuits.py:72 templates/circuits/circuit.html:56 +msgid "Commit Rate" +msgstr "" + +#: circuits/tables/circuits.py:75 circuits/tables/providers.py:48 +#: circuits/tables/providers.py:82 circuits/tables/providers.py:107 +#: dcim/tables/devices.py:1016 dcim/tables/devicetypes.py:92 +#: dcim/tables/modules.py:29 dcim/tables/modules.py:72 dcim/tables/power.py:39 +#: dcim/tables/power.py:91 dcim/tables/racks.py:76 dcim/tables/racks.py:156 +#: dcim/tables/sites.py:103 extras/forms/bulk_edit.py:299 +#: extras/tables/tables.py:485 ipam/tables/asn.py:68 ipam/tables/fhrp.py:34 +#: ipam/tables/ip.py:135 ipam/tables/ip.py:272 ipam/tables/ip.py:325 +#: ipam/tables/ip.py:392 ipam/tables/l2vpn.py:37 ipam/tables/services.py:24 +#: ipam/tables/services.py:54 ipam/tables/vlans.py:141 ipam/tables/vrfs.py:46 +#: ipam/tables/vrfs.py:71 templates/dcim/cable_edit.html:85 +#: templates/generic/bulk_edit.html:102 templates/inc/panels/comments.html:6 +#: tenancy/tables/contacts.py:68 tenancy/tables/tenants.py:46 +#: utilities/forms/fields/fields.py:29 virtualization/tables/clusters.py:91 +#: virtualization/tables/virtualmachines.py:66 +#: wireless/tables/wirelesslan.py:27 wireless/tables/wirelesslan.py:58 +msgid "Comments" +msgstr "" + +#: circuits/tables/providers.py:23 +msgid "Accounts" +msgstr "" + +#: circuits/tables/providers.py:29 +msgid "Account Count" +msgstr "" + +#: circuits/tables/providers.py:39 dcim/tables/sites.py:100 +msgid "ASN Count" +msgstr "" + +#: core/choices.py:18 +msgid "New" +msgstr "" + +#: core/choices.py:19 +msgid "Queued" +msgstr "" + +#: core/choices.py:20 +msgid "Syncing" +msgstr "" + +#: core/choices.py:21 core/choices.py:57 core/tables/jobs.py:40 +#: extras/choices.py:199 templates/core/job.html:69 +msgid "Completed" +msgstr "" + +#: core/choices.py:22 core/choices.py:59 dcim/choices.py:176 +#: dcim/choices.py:222 dcim/choices.py:1496 extras/choices.py:201 +#: virtualization/choices.py:47 +msgid "Failed" +msgstr "" + +#: core/choices.py:35 netbox/navigation/menu.py:311 +#: templates/extras/script/base.html:14 templates/extras/script_list.html:6 +#: templates/extras/script_list.html:20 templates/extras/script_result.html:18 +msgid "Scripts" +msgstr "" + +#: core/choices.py:36 netbox/navigation/menu.py:305 +#: templates/extras/report/base.html:13 templates/extras/report_list.html:7 +#: templates/extras/report_list.html:12 +msgid "Reports" +msgstr "" + +#: core/choices.py:54 extras/choices.py:196 +msgid "Pending" +msgstr "" + +#: core/choices.py:55 core/tables/jobs.py:31 extras/choices.py:197 +#: templates/core/job.html:56 +msgid "Scheduled" +msgstr "" + +#: core/choices.py:56 extras/choices.py:198 +msgid "Running" +msgstr "" + +#: core/choices.py:58 extras/choices.py:200 +msgid "Errored" +msgstr "" + +#: core/data_backends.py:29 templates/dcim/interface.html:220 +msgid "Local" +msgstr "" + +#: core/data_backends.py:47 extras/tables/tables.py:431 +#: templates/account/profile.html:16 templates/users/user.html:18 +#: users/tables.py:31 +msgid "Username" +msgstr "" + +#: core/data_backends.py:49 core/data_backends.py:55 +msgid "Only used for cloning with HTTP(S)" +msgstr "" + +#: core/data_backends.py:53 templates/account/base.html:17 +#: templates/account/password.html:11 users/forms/model_forms.py:171 +msgid "Password" +msgstr "" + +#: core/data_backends.py:59 +msgid "Branch" +msgstr "" + +#: core/data_backends.py:118 +msgid "AWS access key ID" +msgstr "" + +#: core/data_backends.py:122 +msgid "AWS secret access key" +msgstr "" + +#: core/filtersets.py:48 extras/filtersets.py:172 extras/filtersets.py:507 +#: extras/filtersets.py:535 +msgid "Data source (ID)" +msgstr "" + +#: core/filtersets.py:54 +msgid "Data source (name)" +msgstr "" + +#: core/forms/bulk_edit.py:24 ipam/forms/bulk_edit.py:49 +msgid "Enforce unique space" +msgstr "" + +#: core/forms/bulk_edit.py:33 extras/forms/model_forms.py:196 +#: templates/extras/savedfilter.html:57 +msgid "Parameters" +msgstr "" + +#: core/forms/bulk_edit.py:37 templates/core/datasource.html:69 +msgid "Ignore rules" +msgstr "" + +#: core/forms/filtersets.py:27 core/forms/model_forms.py:89 +#: extras/forms/model_forms.py:159 extras/forms/model_forms.py:352 +#: extras/forms/model_forms.py:405 extras/tables/tables.py:171 +#: extras/tables/tables.py:363 extras/tables/tables.py:398 +#: templates/core/datasource.html:31 +#: templates/dcim/device/render_config.html:19 +#: templates/extras/configcontext.html:30 +#: templates/extras/configtemplate.html:22 +#: templates/extras/exporttemplate.html:41 +#: templates/virtualization/virtualmachine/render_config.html:19 +msgid "Data Source" +msgstr "" + +#: core/forms/filtersets.py:40 core/tables/data.py:26 +#: dcim/forms/bulk_edit.py:1012 dcim/forms/bulk_edit.py:1285 +#: dcim/forms/filtersets.py:1261 dcim/tables/devices.py:562 +#: dcim/tables/devicetypes.py:221 extras/forms/bulk_edit.py:92 +#: extras/forms/bulk_edit.py:156 extras/forms/bulk_edit.py:177 +#: extras/forms/filtersets.py:116 extras/forms/filtersets.py:203 +#: extras/forms/filtersets.py:242 extras/tables/tables.py:144 +#: extras/tables/tables.py:233 extras/tables/tables.py:280 +#: templates/core/datasource.html:43 templates/dcim/interface.html:62 +#: templates/extras/customlink.html:18 templates/extras/savedfilter.html:26 +#: templates/extras/webhook.html:20 templates/users/objectpermission.html:26 +#: templates/virtualization/vminterface.html:32 users/forms/bulk_edit.py:69 +#: users/forms/filtersets.py:73 users/tables.py:86 +#: virtualization/forms/bulk_edit.py:214 virtualization/forms/filtersets.py:203 +msgid "Enabled" +msgstr "" + +#: core/forms/filtersets.py:52 core/forms/mixins.py:21 +msgid "File" +msgstr "" + +#: core/forms/filtersets.py:57 core/forms/mixins.py:16 +#: extras/forms/filtersets.py:144 extras/forms/filtersets.py:311 +#: extras/forms/filtersets.py:397 +msgid "Data source" +msgstr "" + +#: core/forms/filtersets.py:65 extras/forms/filtersets.py:424 +msgid "Creation" +msgstr "" + +#: core/forms/filtersets.py:71 extras/forms/filtersets.py:448 +#: extras/forms/filtersets.py:494 extras/tables/tables.py:474 +#: ipam/tables/l2vpn.py:59 templates/core/job.html:25 +#: templates/extras/objectchange.html:56 tenancy/tables/contacts.py:90 +msgid "Object Type" +msgstr "" + +#: core/forms/filtersets.py:81 +msgid "Created after" +msgstr "" + +#: core/forms/filtersets.py:86 +msgid "Created before" +msgstr "" + +#: core/forms/filtersets.py:91 +msgid "Scheduled after" +msgstr "" + +#: core/forms/filtersets.py:96 +msgid "Scheduled before" +msgstr "" + +#: core/forms/filtersets.py:101 +msgid "Started after" +msgstr "" + +#: core/forms/filtersets.py:106 +msgid "Started before" +msgstr "" + +#: core/forms/filtersets.py:111 +msgid "Completed after" +msgstr "" + +#: core/forms/filtersets.py:116 +msgid "Completed before" +msgstr "" + +#: core/forms/filtersets.py:123 dcim/forms/bulk_edit.py:359 +#: dcim/forms/filtersets.py:349 dcim/forms/filtersets.py:393 +#: dcim/forms/model_forms.py:251 extras/forms/filtersets.py:440 +#: extras/forms/filtersets.py:486 templates/dcim/rackreservation.html:65 +#: templates/extras/objectchange.html:40 templates/extras/savedfilter.html:22 +#: templates/users/token.html:22 templates/users/user.html:6 +#: templates/users/user.html:14 users/filtersets.py:74 users/filtersets.py:134 +#: users/forms/filtersets.py:87 users/forms/filtersets.py:128 +#: users/forms/model_forms.py:156 users/forms/model_forms.py:194 +#: users/tables.py:19 +msgid "User" +msgstr "" + +#: core/forms/model_forms.py:46 core/tables/data.py:46 +#: templates/core/datafile.html:36 templates/extras/report/base.html:33 +#: templates/extras/script/base.html:32 templates/extras/script_result.html:45 +msgid "Source" +msgstr "" + +#: core/forms/model_forms.py:50 +msgid "Backend Parameters" +msgstr "" + +#: core/forms/model_forms.py:88 +msgid "File Upload" +msgstr "" + +#: core/models/data.py:47 dcim/models/cables.py:44 +#: dcim/models/device_component_templates.py:178 +#: dcim/models/device_component_templates.py:212 +#: dcim/models/device_component_templates.py:247 +#: dcim/models/device_component_templates.py:309 +#: dcim/models/device_component_templates.py:388 +#: dcim/models/device_component_templates.py:487 +#: dcim/models/device_component_templates.py:587 +#: dcim/models/device_components.py:285 dcim/models/device_components.py:314 +#: dcim/models/device_components.py:347 dcim/models/device_components.py:465 +#: dcim/models/device_components.py:603 dcim/models/device_components.py:962 +#: dcim/models/device_components.py:1036 dcim/models/power.py:101 +#: dcim/models/racks.py:127 extras/models/customfields.py:69 +#: extras/models/search.py:41 ipam/models/l2vpn.py:32 +#: virtualization/models/clusters.py:61 +msgid "type" +msgstr "" + +#: core/models/data.py:52 extras/choices.py:34 extras/models/models.py:86 +#: templates/core/datasource.html:59 +msgid "URL" +msgstr "" + +#: core/models/data.py:62 dcim/models/device_component_templates.py:393 +#: dcim/models/device_components.py:514 extras/models/models.py:93 +#: extras/models/models.py:248 extras/models/models.py:473 users/models.py:350 +msgid "enabled" +msgstr "" + +#: core/models/data.py:66 +msgid "ignore rules" +msgstr "" + +#: core/models/data.py:68 +msgid "Patterns (one per line) matching files to ignore when syncing" +msgstr "" + +#: core/models/data.py:71 extras/models/models.py:481 +msgid "parameters" +msgstr "" + +#: core/models/data.py:76 +msgid "last synced" +msgstr "" + +#: core/models/data.py:84 +msgid "data source" +msgstr "" + +#: core/models/data.py:85 +msgid "data sources" +msgstr "" + +#: core/models/data.py:124 +#, python-brace-format +msgid "Unknown backend type: {type}" +msgstr "" + +#: core/models/data.py:259 core/models/files.py:26 core/models/jobs.py:50 +#: extras/models/models.py:663 extras/models/models.py:704 +#: netbox/models/features.py:51 users/models.py:245 +msgid "created" +msgstr "" + +#: core/models/data.py:263 core/models/files.py:30 netbox/models/features.py:57 +msgid "last updated" +msgstr "" + +#: core/models/data.py:273 dcim/models/cables.py:417 +msgid "path" +msgstr "" + +#: core/models/data.py:276 +msgid "File path relative to the data source's root" +msgstr "" + +#: core/models/data.py:280 ipam/models/ip.py:502 +msgid "size" +msgstr "" + +#: core/models/data.py:283 +msgid "hash" +msgstr "" + +#: core/models/data.py:287 +msgid "Length must be 64 hexadecimal characters." +msgstr "" + +#: core/models/data.py:289 +msgid "SHA256 hash of the file data" +msgstr "" + +#: core/models/data.py:306 +msgid "data file" +msgstr "" + +#: core/models/data.py:307 +msgid "data files" +msgstr "" + +#: core/models/data.py:391 +msgid "auto sync record" +msgstr "" + +#: core/models/data.py:392 +msgid "auto sync records" +msgstr "" + +#: core/models/files.py:36 +msgid "file root" +msgstr "" + +#: core/models/files.py:41 +msgid "file path" +msgstr "" + +#: core/models/files.py:43 +msgid "File path relative to the designated root path" +msgstr "" + +#: core/models/files.py:59 +msgid "managed file" +msgstr "" + +#: core/models/files.py:60 +msgid "managed files" +msgstr "" + +#: core/models/jobs.py:54 +msgid "scheduled" +msgstr "" + +#: core/models/jobs.py:59 +msgid "interval" +msgstr "" + +#: core/models/jobs.py:65 +msgid "Recurrence interval (in minutes)" +msgstr "" + +#: core/models/jobs.py:68 +msgid "started" +msgstr "" + +#: core/models/jobs.py:73 +msgid "completed" +msgstr "" + +#: core/models/jobs.py:91 extras/models/staging.py:87 +msgid "data" +msgstr "" + +#: core/models/jobs.py:96 +msgid "job ID" +msgstr "" + +#: core/models/jobs.py:104 +msgid "job" +msgstr "" + +#: core/models/jobs.py:105 +msgid "jobs" +msgstr "" + +#: core/tables/data.py:50 templates/core/datafile.html:40 +msgid "Path" +msgstr "" + +#: core/tables/data.py:54 templates/extras/inc/result_pending.html:7 +msgid "Last updated" +msgstr "" + +#: core/tables/jobs.py:10 dcim/tables/devicetypes.py:161 +#: extras/tables/tables.py:196 extras/tables/tables.py:340 +#: netbox/tables/tables.py:180 templates/dcim/virtualchassis_edit.html:53 +#: wireless/tables/wirelesslink.py:16 +msgid "ID" +msgstr "" + +#: core/tables/jobs.py:21 extras/choices.py:38 extras/tables/tables.py:258 +#: extras/tables/tables.py:350 extras/tables/tables.py:448 +#: extras/tables/tables.py:479 ipam/tables/l2vpn.py:64 +#: netbox/tables/tables.py:229 templates/extras/htmx/report_result.html:45 +#: templates/extras/journalentry.html:21 templates/extras/objectchange.html:62 +#: tenancy/tables/contacts.py:93 +msgid "Object" +msgstr "" + +#: core/tables/jobs.py:34 +msgid "Interval" +msgstr "" + +#: core/tables/jobs.py:37 templates/core/job.html:65 +#: templates/extras/htmx/report_result.html:7 +#: templates/extras/htmx/script_result.html:8 +msgid "Started" +msgstr "" + +#: dcim/api/serializers.py:205 templates/dcim/rack.html:40 +msgid "Facility ID" +msgstr "" + +#: dcim/api/serializers.py:321 dcim/api/serializers.py:680 +msgid "Position (U)" +msgstr "" + +#: dcim/choices.py:21 virtualization/choices.py:21 +msgid "Staging" +msgstr "" + +#: dcim/choices.py:23 dcim/choices.py:178 dcim/choices.py:223 +#: dcim/choices.py:1420 virtualization/choices.py:23 +#: virtualization/choices.py:48 +msgid "Decommissioning" +msgstr "" + +#: dcim/choices.py:24 +msgid "Retired" +msgstr "" + +#: dcim/choices.py:65 +msgid "2-post frame" +msgstr "" + +#: dcim/choices.py:66 +msgid "4-post frame" +msgstr "" + +#: dcim/choices.py:67 +msgid "4-post cabinet" +msgstr "" + +#: dcim/choices.py:68 +msgid "Wall-mounted frame" +msgstr "" + +#: dcim/choices.py:69 +msgid "Wall-mounted frame (vertical)" +msgstr "" + +#: dcim/choices.py:70 +msgid "Wall-mounted cabinet" +msgstr "" + +#: dcim/choices.py:71 +msgid "Wall-mounted cabinet (vertical)" +msgstr "" + +#: dcim/choices.py:83 dcim/choices.py:84 dcim/choices.py:85 dcim/choices.py:86 +#, python-brace-format +msgid "{n} inches" +msgstr "" + +#: 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 "" + +#: dcim/choices.py:101 templates/dcim/device.html:279 +msgid "Available" +msgstr "" + +#: 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 "" + +#: dcim/choices.py:114 templates/dcim/rack.html:135 +msgid "Millimeters" +msgstr "" + +#: dcim/choices.py:115 dcim/choices.py:1442 +msgid "Inches" +msgstr "" + +#: dcim/choices.py:140 dcim/forms/bulk_edit.py:66 dcim/forms/bulk_edit.py:85 +#: dcim/forms/bulk_edit.py:171 dcim/forms/bulk_edit.py:1290 +#: dcim/forms/bulk_import.py:59 dcim/forms/bulk_import.py:73 +#: dcim/forms/bulk_import.py:136 dcim/forms/bulk_import.py:503 +#: dcim/forms/bulk_import.py:770 dcim/forms/bulk_import.py:1021 +#: dcim/forms/filtersets.py:224 dcim/forms/model_forms.py:73 +#: dcim/forms/model_forms.py:94 dcim/forms/model_forms.py:172 +#: dcim/forms/model_forms.py:954 dcim/forms/model_forms.py:1295 +#: dcim/forms/object_import.py:181 dcim/tables/devices.py:654 +#: extras/tables/tables.py:203 ipam/tables/fhrp.py:59 ipam/tables/ip.py:374 +#: ipam/tables/services.py:44 templates/dcim/interface.html:97 +#: templates/dcim/interface.html:317 templates/dcim/location.html:44 +#: templates/dcim/region.html:38 templates/dcim/sitegroup.html:38 +#: templates/ipam/service.html:31 templates/tenancy/contactgroup.html:32 +#: templates/tenancy/tenantgroup.html:40 +#: templates/virtualization/vminterface.html:42 +#: templates/wireless/wirelesslangroup.html:38 tenancy/forms/bulk_edit.py:26 +#: tenancy/forms/bulk_edit.py:60 tenancy/forms/bulk_import.py:24 +#: tenancy/forms/bulk_import.py:58 tenancy/forms/model_forms.py:27 +#: tenancy/forms/model_forms.py:72 virtualization/forms/bulk_edit.py:204 +#: virtualization/forms/bulk_import.py:150 +#: virtualization/tables/virtualmachines.py:136 wireless/forms/bulk_edit.py:23 +#: wireless/forms/bulk_import.py:21 wireless/forms/model_forms.py:20 +msgid "Parent" +msgstr "" + +#: dcim/choices.py:141 +msgid "Child" +msgstr "" + +#: dcim/choices.py:155 templates/dcim/device.html:362 +#: templates/dcim/rack.html:188 templates/dcim/rack_elevation_list.html:22 +#: templates/dcim/rackreservation.html:84 +msgid "Front" +msgstr "" + +#: dcim/choices.py:156 templates/dcim/device.html:368 +#: templates/dcim/rack.html:194 templates/dcim/rack_elevation_list.html:23 +#: templates/dcim/rackreservation.html:90 +msgid "Rear" +msgstr "" + +#: dcim/choices.py:175 dcim/choices.py:221 virtualization/choices.py:46 +msgid "Staged" +msgstr "" + +#: dcim/choices.py:177 +msgid "Inventory" +msgstr "" + +#: dcim/choices.py:193 +msgid "Front to rear" +msgstr "" + +#: dcim/choices.py:194 +msgid "Rear to front" +msgstr "" + +#: dcim/choices.py:195 +msgid "Left to right" +msgstr "" + +#: dcim/choices.py:196 +msgid "Right to left" +msgstr "" + +#: dcim/choices.py:197 +msgid "Side to rear" +msgstr "" + +#: dcim/choices.py:198 dcim/choices.py:1215 +msgid "Passive" +msgstr "" + +#: dcim/choices.py:199 +msgid "Mixed" +msgstr "" + +#: dcim/choices.py:443 dcim/choices.py:680 +msgid "NEMA (Non-locking)" +msgstr "" + +#: dcim/choices.py:465 dcim/choices.py:702 +msgid "NEMA (Locking)" +msgstr "" + +#: dcim/choices.py:488 dcim/choices.py:725 +msgid "California Style" +msgstr "" + +#: dcim/choices.py:496 +msgid "International/ITA" +msgstr "" + +#: dcim/choices.py:526 dcim/choices.py:755 +msgid "Proprietary" +msgstr "" + +#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1131 +#: dcim/choices.py:1133 dcim/choices.py:1338 dcim/choices.py:1340 +#: netbox/navigation/menu.py:188 +msgid "Other" +msgstr "" + +#: dcim/choices.py:733 +msgid "ITA/International" +msgstr "" + +#: dcim/choices.py:794 +msgid "Physical" +msgstr "" + +#: dcim/choices.py:795 dcim/choices.py:949 +msgid "Virtual" +msgstr "" + +#: dcim/choices.py:796 dcim/choices.py:1019 dcim/forms/bulk_edit.py:1398 +#: dcim/forms/filtersets.py:1225 dcim/forms/model_forms.py:880 +#: dcim/forms/model_forms.py:1189 netbox/navigation/menu.py:128 +#: netbox/navigation/menu.py:132 templates/dcim/interface.html:213 +msgid "Wireless" +msgstr "" + +#: dcim/choices.py:947 +msgid "Virtual interfaces" +msgstr "" + +#: dcim/choices.py:950 dcim/forms/bulk_edit.py:1295 +#: dcim/forms/bulk_import.py:777 dcim/forms/model_forms.py:868 +#: dcim/tables/devices.py:658 templates/dcim/interface.html:101 +#: templates/virtualization/vminterface.html:46 +#: virtualization/forms/bulk_edit.py:209 +#: virtualization/forms/bulk_import.py:157 +#: virtualization/tables/virtualmachines.py:140 +msgid "Bridge" +msgstr "" + +#: dcim/choices.py:951 +msgid "Link Aggregation Group (LAG)" +msgstr "" + +#: dcim/choices.py:955 +msgid "Ethernet (fixed)" +msgstr "" + +#: dcim/choices.py:969 +msgid "Ethernet (modular)" +msgstr "" + +#: dcim/choices.py:1005 +msgid "Ethernet (backplane)" +msgstr "" + +#: dcim/choices.py:1033 +msgid "Cellular" +msgstr "" + +#: dcim/choices.py:1080 dcim/forms/filtersets.py:299 +#: dcim/forms/filtersets.py:729 dcim/forms/filtersets.py:869 +#: dcim/forms/filtersets.py:1417 templates/dcim/inventoryitem.html:53 +#: templates/dcim/virtualchassis_edit.html:55 +msgid "Serial" +msgstr "" + +#: dcim/choices.py:1095 +msgid "Coaxial" +msgstr "" + +#: dcim/choices.py:1112 +msgid "Stacking" +msgstr "" + +#: dcim/choices.py:1162 +msgid "Half" +msgstr "" + +#: dcim/choices.py:1163 +msgid "Full" +msgstr "" + +#: dcim/choices.py:1164 wireless/choices.py:480 +msgid "Auto" +msgstr "" + +#: dcim/choices.py:1175 +msgid "Access" +msgstr "" + +#: dcim/choices.py:1176 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 +#: templates/dcim/inc/interface_vlans_table.html:7 +msgid "Tagged" +msgstr "" + +#: dcim/choices.py:1177 +msgid "Tagged (All)" +msgstr "" + +#: dcim/choices.py:1206 +msgid "IEEE Standard" +msgstr "" + +#: dcim/choices.py:1217 +msgid "Passive 24V (2-pair)" +msgstr "" + +#: dcim/choices.py:1218 +msgid "Passive 24V (4-pair)" +msgstr "" + +#: dcim/choices.py:1219 +msgid "Passive 48V (2-pair)" +msgstr "" + +#: dcim/choices.py:1220 +msgid "Passive 48V (4-pair)" +msgstr "" + +#: dcim/choices.py:1282 dcim/choices.py:1378 +msgid "Copper" +msgstr "" + +#: dcim/choices.py:1305 +msgid "Fiber Optic" +msgstr "" + +#: dcim/choices.py:1394 +msgid "Fiber" +msgstr "" + +#: dcim/choices.py:1407 dcim/forms/bulk_edit.py:859 +#: dcim/forms/bulk_edit.py:1242 dcim/forms/bulk_edit.py:1260 +#: dcim/tables/racks.py:89 extras/forms/model_forms.py:489 +#: netbox/navigation/menu.py:257 netbox/navigation/menu.py:261 +msgid "Power" +msgstr "" + +#: dcim/choices.py:1418 dcim/forms/filtersets.py:1132 +msgid "Connected" +msgstr "" + +#: dcim/choices.py:1437 +msgid "Kilometers" +msgstr "" + +#: dcim/choices.py:1438 templates/dcim/cable_trace.html:62 +msgid "Meters" +msgstr "" + +#: dcim/choices.py:1439 +msgid "Centimeters" +msgstr "" + +#: dcim/choices.py:1440 +msgid "Miles" +msgstr "" + +#: dcim/choices.py:1441 templates/dcim/cable_trace.html:63 +msgid "Feet" +msgstr "" + +#: dcim/choices.py:1457 templates/dcim/device.html:349 +#: templates/dcim/rack.html:164 +msgid "Kilograms" +msgstr "" + +#: dcim/choices.py:1458 +msgid "Grams" +msgstr "" + +#: dcim/choices.py:1459 templates/dcim/rack.html:165 +msgid "Pounds" +msgstr "" + +#: dcim/choices.py:1460 +msgid "Ounces" +msgstr "" + +#: dcim/choices.py:1506 tenancy/choices.py:17 +msgid "Primary" +msgstr "" + +#: dcim/choices.py:1507 +msgid "Redundant" +msgstr "" + +#: dcim/choices.py:1528 +msgid "Single phase" +msgstr "" + +#: dcim/choices.py:1529 +msgid "Three-phase" +msgstr "" + +#: dcim/filtersets.py:78 +msgid "Parent region (ID)" +msgstr "" + +#: dcim/filtersets.py:84 +msgid "Parent region (slug)" +msgstr "" + +#: dcim/filtersets.py:95 +msgid "Parent site group (ID)" +msgstr "" + +#: dcim/filtersets.py:101 +msgid "Parent site group (slug)" +msgstr "" + +#: dcim/filtersets.py:130 ipam/filtersets.py:792 ipam/filtersets.py:925 +msgid "Group (ID)" +msgstr "" + +#: dcim/filtersets.py:136 +msgid "Group (slug)" +msgstr "" + +#: dcim/filtersets.py:142 dcim/filtersets.py:147 +msgid "AS (ID)" +msgstr "" + +#: dcim/filtersets.py:215 dcim/filtersets.py:290 dcim/filtersets.py:388 +#: dcim/filtersets.py:909 dcim/filtersets.py:1215 dcim/filtersets.py:1883 +msgid "Location (ID)" +msgstr "" + +#: dcim/filtersets.py:222 dcim/filtersets.py:297 dcim/filtersets.py:395 +#: dcim/filtersets.py:1221 extras/filtersets.py:416 +msgid "Location (slug)" +msgstr "" + +#: dcim/filtersets.py:311 dcim/filtersets.py:762 dcim/filtersets.py:846 +#: dcim/filtersets.py:1621 ipam/filtersets.py:346 ipam/filtersets.py:458 +#: ipam/filtersets.py:935 virtualization/filtersets.py:206 +msgid "Role (ID)" +msgstr "" + +#: dcim/filtersets.py:317 dcim/filtersets.py:768 dcim/filtersets.py:852 +#: dcim/filtersets.py:1627 extras/filtersets.py:432 ipam/filtersets.py:352 +#: ipam/filtersets.py:464 ipam/filtersets.py:941 +#: virtualization/filtersets.py:212 +msgid "Role (slug)" +msgstr "" + +#: dcim/filtersets.py:345 dcim/filtersets.py:914 dcim/filtersets.py:1226 +#: dcim/filtersets.py:1944 +msgid "Rack (ID)" +msgstr "" + +#: dcim/filtersets.py:399 extras/filtersets.py:203 extras/filtersets.py:247 +#: extras/filtersets.py:287 extras/filtersets.py:582 +msgid "User (ID)" +msgstr "" + +#: dcim/filtersets.py:405 extras/filtersets.py:209 extras/filtersets.py:253 +#: extras/filtersets.py:293 users/filtersets.py:80 users/filtersets.py:140 +msgid "User (name)" +msgstr "" + +#: dcim/filtersets.py:433 dcim/filtersets.py:559 dcim/filtersets.py:752 +#: dcim/filtersets.py:803 dcim/filtersets.py:825 dcim/filtersets.py:1118 +#: dcim/filtersets.py:1611 +msgid "Manufacturer (ID)" +msgstr "" + +#: dcim/filtersets.py:439 dcim/filtersets.py:565 dcim/filtersets.py:758 +#: dcim/filtersets.py:809 dcim/filtersets.py:831 dcim/filtersets.py:1124 +#: dcim/filtersets.py:1617 +msgid "Manufacturer (slug)" +msgstr "" + +#: dcim/filtersets.py:443 +msgid "Default platform (ID)" +msgstr "" + +#: dcim/filtersets.py:449 +msgid "Default platform (slug)" +msgstr "" + +#: dcim/filtersets.py:452 dcim/forms/filtersets.py:448 +msgid "Has a front image" +msgstr "" + +#: dcim/filtersets.py:456 dcim/forms/filtersets.py:455 +msgid "Has a rear image" +msgstr "" + +#: dcim/filtersets.py:461 dcim/filtersets.py:569 dcim/filtersets.py:967 +#: dcim/forms/filtersets.py:462 dcim/forms/filtersets.py:558 +#: dcim/forms/filtersets.py:768 +msgid "Has console ports" +msgstr "" + +#: dcim/filtersets.py:465 dcim/filtersets.py:573 dcim/filtersets.py:971 +#: dcim/forms/filtersets.py:469 dcim/forms/filtersets.py:565 +#: dcim/forms/filtersets.py:775 +msgid "Has console server ports" +msgstr "" + +#: dcim/filtersets.py:469 dcim/filtersets.py:577 dcim/filtersets.py:975 +#: dcim/forms/filtersets.py:476 dcim/forms/filtersets.py:572 +#: dcim/forms/filtersets.py:782 +msgid "Has power ports" +msgstr "" + +#: dcim/filtersets.py:473 dcim/filtersets.py:581 dcim/filtersets.py:979 +#: dcim/forms/filtersets.py:483 dcim/forms/filtersets.py:579 +#: dcim/forms/filtersets.py:789 +msgid "Has power outlets" +msgstr "" + +#: dcim/filtersets.py:477 dcim/filtersets.py:585 dcim/filtersets.py:983 +#: dcim/forms/filtersets.py:490 dcim/forms/filtersets.py:586 +#: dcim/forms/filtersets.py:796 +msgid "Has interfaces" +msgstr "" + +#: dcim/filtersets.py:481 dcim/filtersets.py:589 dcim/filtersets.py:987 +#: dcim/forms/filtersets.py:497 dcim/forms/filtersets.py:593 +#: dcim/forms/filtersets.py:803 +msgid "Has pass-through ports" +msgstr "" + +#: dcim/filtersets.py:485 dcim/filtersets.py:991 dcim/forms/filtersets.py:511 +msgid "Has module bays" +msgstr "" + +#: dcim/filtersets.py:489 dcim/filtersets.py:995 dcim/forms/filtersets.py:504 +msgid "Has device bays" +msgstr "" + +#: dcim/filtersets.py:493 dcim/forms/filtersets.py:518 +msgid "Has inventory items" +msgstr "" + +#: dcim/filtersets.py:636 dcim/filtersets.py:841 dcim/filtersets.py:1247 +msgid "Device type (ID)" +msgstr "" + +#: dcim/filtersets.py:649 dcim/filtersets.py:1129 +msgid "Module type (ID)" +msgstr "" + +#: dcim/filtersets.py:748 dcim/filtersets.py:1607 +msgid "Parent inventory item (ID)" +msgstr "" + +#: dcim/filtersets.py:791 dcim/filtersets.py:813 dcim/filtersets.py:963 +#: virtualization/filtersets.py:234 +msgid "Config template (ID)" +msgstr "" + +#: dcim/filtersets.py:837 +msgid "Device type (slug)" +msgstr "" + +#: dcim/filtersets.py:857 +msgid "Parent Device (ID)" +msgstr "" + +#: dcim/filtersets.py:861 virtualization/filtersets.py:216 +msgid "Platform (ID)" +msgstr "" + +#: dcim/filtersets.py:867 extras/filtersets.py:443 +#: virtualization/filtersets.py:222 +msgid "Platform (slug)" +msgstr "" + +#: dcim/filtersets.py:903 dcim/filtersets.py:1210 dcim/filtersets.py:1705 +#: dcim/filtersets.py:1877 dcim/filtersets.py:1935 +msgid "Site name (slug)" +msgstr "" + +#: dcim/filtersets.py:918 +msgid "VM cluster (ID)" +msgstr "" + +#: dcim/filtersets.py:924 +msgid "Device model (slug)" +msgstr "" + +#: dcim/filtersets.py:935 dcim/forms/bulk_edit.py:421 +msgid "Is full depth" +msgstr "" + +#: dcim/filtersets.py:939 dcim/forms/common.py:18 dcim/forms/filtersets.py:738 +#: dcim/forms/filtersets.py:1276 dcim/models/device_components.py:520 +#: virtualization/filtersets.py:226 virtualization/filtersets.py:292 +#: virtualization/forms/filtersets.py:165 +#: virtualization/forms/filtersets.py:211 +msgid "MAC address" +msgstr "" + +#: dcim/filtersets.py:946 dcim/forms/filtersets.py:747 +#: dcim/forms/filtersets.py:834 virtualization/filtersets.py:230 +#: virtualization/forms/filtersets.py:169 +msgid "Has a primary IP" +msgstr "" + +#: dcim/filtersets.py:950 +msgid "Has an out-of-band IP" +msgstr "" + +#: dcim/filtersets.py:955 +msgid "Virtual chassis (ID)" +msgstr "" + +#: dcim/filtersets.py:959 +msgid "Is a virtual chassis member" +msgstr "" + +#: dcim/filtersets.py:1000 +msgid "Primary IPv4 (ID)" +msgstr "" + +#: dcim/filtersets.py:1005 +msgid "Primary IPv6 (ID)" +msgstr "" + +#: dcim/filtersets.py:1010 +msgid "OOB IP (ID)" +msgstr "" + +#: dcim/filtersets.py:1135 +msgid "Module type (model)" +msgstr "" + +#: dcim/filtersets.py:1141 +msgid "Module Bay (ID)" +msgstr "" + +#: dcim/filtersets.py:1145 dcim/filtersets.py:1236 ipam/filtersets.py:567 +#: ipam/filtersets.py:802 ipam/filtersets.py:1010 ipam/filtersets.py:1143 +#: virtualization/filtersets.py:157 +msgid "Device (ID)" +msgstr "" + +#: dcim/filtersets.py:1232 +msgid "Rack (name)" +msgstr "" + +#: dcim/filtersets.py:1242 ipam/filtersets.py:562 ipam/filtersets.py:797 +#: ipam/filtersets.py:1016 ipam/filtersets.py:1138 +msgid "Device (name)" +msgstr "" + +#: dcim/filtersets.py:1253 +msgid "Device type (model)" +msgstr "" + +#: dcim/filtersets.py:1258 dcim/filtersets.py:1281 +msgid "Device role (ID)" +msgstr "" + +#: dcim/filtersets.py:1264 dcim/filtersets.py:1287 +msgid "Device role (slug)" +msgstr "" + +#: dcim/filtersets.py:1269 +msgid "Virtual Chassis (ID)" +msgstr "" + +#: dcim/filtersets.py:1275 dcim/forms/filtersets.py:105 +#: dcim/tables/devices.py:235 netbox/navigation/menu.py:67 +#: templates/dcim/device.html:140 templates/dcim/device_edit.html:93 +#: templates/dcim/virtualchassis.html:20 +#: templates/dcim/virtualchassis_add.html:8 +#: templates/dcim/virtualchassis_edit.html:25 +msgid "Virtual Chassis" +msgstr "" + +#: dcim/filtersets.py:1307 +msgid "Module (ID)" +msgstr "" + +#: dcim/filtersets.py:1411 ipam/forms/bulk_import.py:191 +#: ipam/forms/bulk_import.py:568 +msgid "Assigned VLAN" +msgstr "" + +#: dcim/filtersets.py:1415 +msgid "Assigned VID" +msgstr "" + +#: dcim/filtersets.py:1420 dcim/forms/bulk_edit.py:1374 +#: dcim/forms/bulk_import.py:828 dcim/forms/filtersets.py:1319 +#: dcim/forms/model_forms.py:1174 dcim/models/device_components.py:709 +#: dcim/tables/devices.py:625 ipam/filtersets.py:281 ipam/filtersets.py:292 +#: ipam/filtersets.py:448 ipam/filtersets.py:540 ipam/filtersets.py:551 +#: ipam/forms/bulk_edit.py:228 ipam/forms/bulk_edit.py:283 +#: ipam/forms/bulk_edit.py:325 ipam/forms/bulk_import.py:159 +#: ipam/forms/bulk_import.py:245 ipam/forms/bulk_import.py:281 +#: ipam/forms/filtersets.py:70 ipam/forms/filtersets.py:171 +#: ipam/forms/filtersets.py:299 ipam/forms/model_forms.py:61 +#: ipam/forms/model_forms.py:205 ipam/forms/model_forms.py:248 +#: ipam/forms/model_forms.py:292 ipam/forms/model_forms.py:414 +#: ipam/forms/model_forms.py:428 ipam/forms/model_forms.py:442 +#: ipam/models/ip.py:232 ipam/models/ip.py:511 ipam/models/ip.py:719 +#: ipam/models/vrfs.py:62 ipam/tables/ip.py:241 ipam/tables/ip.py:306 +#: ipam/tables/ip.py:356 ipam/tables/ip.py:445 +#: templates/dcim/interface.html:134 templates/ipam/ipaddress.html:21 +#: templates/ipam/iprange.html:43 templates/ipam/prefix.html:19 +#: templates/ipam/vrf.html:7 templates/ipam/vrf.html:14 +#: templates/virtualization/vminterface.html:50 +#: virtualization/forms/bulk_edit.py:258 +#: virtualization/forms/bulk_import.py:170 +#: virtualization/forms/filtersets.py:216 +#: virtualization/forms/model_forms.py:326 +#: virtualization/models/virtualmachines.py:286 +#: virtualization/tables/virtualmachines.py:118 +msgid "VRF" +msgstr "" + +#: dcim/filtersets.py:1426 ipam/filtersets.py:287 ipam/filtersets.py:298 +#: ipam/filtersets.py:454 ipam/filtersets.py:546 ipam/filtersets.py:557 +msgid "VRF (RD)" +msgstr "" + +#: dcim/filtersets.py:1431 ipam/filtersets.py:958 ipam/filtersets.py:1106 +msgid "L2VPN (ID)" +msgstr "" + +#: dcim/filtersets.py:1437 dcim/forms/filtersets.py:1324 +#: dcim/tables/devices.py:579 ipam/filtersets.py:964 +#: ipam/forms/bulk_import.py:540 ipam/forms/filtersets.py:501 +#: ipam/forms/filtersets.py:565 ipam/forms/model_forms.py:779 +#: ipam/forms/model_forms.py:797 ipam/models/l2vpn.py:63 +#: ipam/tables/l2vpn.py:55 ipam/tables/vlans.py:133 +#: templates/dcim/interface.html:109 templates/ipam/l2vpntermination.html:15 +#: templates/ipam/vlan.html:69 virtualization/forms/filtersets.py:221 +msgid "L2VPN" +msgstr "" + +#: dcim/filtersets.py:1469 +msgid "Virtual Chassis Interfaces for Device" +msgstr "" + +#: dcim/filtersets.py:1474 +msgid "Virtual Chassis Interfaces for Device (ID)" +msgstr "" + +#: dcim/filtersets.py:1478 +msgid "Kind of interface" +msgstr "" + +#: dcim/filtersets.py:1483 virtualization/filtersets.py:284 +msgid "Parent interface (ID)" +msgstr "" + +#: dcim/filtersets.py:1488 virtualization/filtersets.py:289 +msgid "Bridged interface (ID)" +msgstr "" + +#: dcim/filtersets.py:1493 +msgid "LAG interface (ID)" +msgstr "" + +#: dcim/filtersets.py:1662 +msgid "Master (ID)" +msgstr "" + +#: dcim/filtersets.py:1668 +msgid "Master (name)" +msgstr "" + +#: dcim/filtersets.py:1710 tenancy/filtersets.py:208 +msgid "Tenant (ID)" +msgstr "" + +#: dcim/filtersets.py:1716 extras/filtersets.py:492 tenancy/filtersets.py:214 +msgid "Tenant (slug)" +msgstr "" + +#: dcim/filtersets.py:1751 dcim/forms/filtersets.py:983 +msgid "Unterminated" +msgstr "" + +#: dcim/filtersets.py:1939 +msgid "Power panel (ID)" +msgstr "" + +#: dcim/forms/bulk_create.py:40 extras/forms/filtersets.py:385 +#: extras/forms/mixins.py:82 extras/forms/model_forms.py:341 +#: extras/forms/model_forms.py:392 netbox/forms/base.py:71 +#: netbox/tables/columns.py:448 +#: templates/circuits/inc/circuit_termination.html:119 +#: templates/generic/bulk_edit.html:81 templates/inc/panels/tags.html:5 +#: utilities/forms/fields/fields.py:81 +msgid "Tags" +msgstr "" + +#: dcim/forms/bulk_create.py:112 dcim/forms/filtersets.py:1381 +#: dcim/forms/model_forms.py:422 dcim/forms/model_forms.py:467 +#: dcim/forms/object_create.py:179 dcim/forms/object_create.py:319 +#: dcim/tables/devices.py:198 dcim/tables/devices.py:703 +#: dcim/tables/devicetypes.py:242 templates/dcim/device.html:62 +#: templates/dcim/device.html:146 templates/dcim/modulebay.html:35 +#: templates/dcim/virtualchassis.html:59 +#: templates/dcim/virtualchassis_edit.html:56 +msgid "Position" +msgstr "" + +#: dcim/forms/bulk_create.py:114 +msgid "" +"Alphanumeric ranges are supported. (Must match the number of names being " +"created.)" +msgstr "" + +#: dcim/forms/bulk_edit.py:115 dcim/forms/bulk_import.py:99 +#: dcim/forms/model_forms.py:120 dcim/tables/sites.py:89 ipam/filtersets.py:931 +#: ipam/forms/bulk_edit.py:530 ipam/forms/bulk_import.py:447 +#: ipam/forms/model_forms.py:511 ipam/tables/fhrp.py:67 +#: ipam/tables/vlans.py:118 ipam/tables/vlans.py:221 +#: templates/dcim/interface.html:290 templates/dcim/site.html:43 +#: templates/ipam/inc/panels/fhrp_groups.html:10 templates/ipam/vlan.html:30 +#: templates/tenancy/contact.html:22 templates/tenancy/tenant.html:21 +#: templates/users/group.html:6 templates/users/group.html:14 +#: templates/virtualization/cluster.html:32 +#: templates/wireless/wirelesslan.html:19 tenancy/forms/bulk_edit.py:42 +#: tenancy/forms/bulk_edit.py:93 tenancy/forms/bulk_import.py:40 +#: tenancy/forms/bulk_import.py:81 tenancy/forms/filtersets.py:48 +#: tenancy/forms/filtersets.py:78 tenancy/forms/filtersets.py:98 +#: tenancy/forms/model_forms.py:49 tenancy/forms/model_forms.py:105 +#: tenancy/forms/model_forms.py:127 tenancy/tables/contacts.py:60 +#: tenancy/tables/tenants.py:42 users/filtersets.py:42 users/filtersets.py:145 +#: users/forms/filtersets.py:34 users/forms/filtersets.py:40 +#: users/forms/filtersets.py:82 virtualization/forms/bulk_edit.py:62 +#: virtualization/forms/bulk_import.py:46 virtualization/forms/filtersets.py:81 +#: virtualization/forms/model_forms.py:68 virtualization/tables/clusters.py:70 +#: wireless/forms/bulk_edit.py:47 wireless/forms/bulk_import.py:36 +#: wireless/forms/filtersets.py:45 wireless/forms/model_forms.py:41 +#: wireless/tables/wirelesslan.py:48 +msgid "Group" +msgstr "" + +#: dcim/forms/bulk_edit.py:130 +msgid "Contact name" +msgstr "" + +#: dcim/forms/bulk_edit.py:135 +msgid "Contact phone" +msgstr "" + +#: dcim/forms/bulk_edit.py:141 +msgid "Contact E-mail" +msgstr "" + +#: dcim/forms/bulk_edit.py:144 dcim/forms/bulk_import.py:122 +#: dcim/forms/model_forms.py:131 +msgid "Time zone" +msgstr "" + +#: dcim/forms/bulk_edit.py:266 dcim/forms/bulk_edit.py:1152 +#: dcim/forms/bulk_edit.py:1539 dcim/forms/bulk_import.py:199 +#: dcim/forms/bulk_import.py:1009 dcim/forms/filtersets.py:296 +#: dcim/forms/filtersets.py:697 dcim/forms/filtersets.py:1408 +#: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:962 +#: dcim/forms/model_forms.py:1303 dcim/forms/object_import.py:186 +#: dcim/tables/devices.py:202 dcim/tables/devices.py:811 +#: dcim/tables/devices.py:922 dcim/tables/devicetypes.py:300 +#: dcim/tables/racks.py:69 extras/filtersets.py:426 ipam/forms/bulk_edit.py:247 +#: ipam/forms/bulk_edit.py:296 ipam/forms/bulk_edit.py:344 +#: ipam/forms/bulk_edit.py:548 ipam/forms/bulk_import.py:199 +#: ipam/forms/bulk_import.py:264 ipam/forms/bulk_import.py:300 +#: ipam/forms/bulk_import.py:466 ipam/forms/filtersets.py:236 +#: ipam/forms/filtersets.py:282 ipam/forms/filtersets.py:349 +#: ipam/forms/filtersets.py:492 ipam/forms/model_forms.py:189 +#: ipam/forms/model_forms.py:224 ipam/forms/model_forms.py:251 +#: ipam/forms/model_forms.py:649 ipam/tables/ip.py:257 ipam/tables/ip.py:313 +#: ipam/tables/ip.py:363 ipam/tables/vlans.py:126 ipam/tables/vlans.py:230 +#: templates/dcim/device.html:204 +#: templates/dcim/inc/panels/inventory_items.html:12 +#: templates/dcim/interface.html:227 templates/dcim/inventoryitem.html:37 +#: templates/dcim/rack.html:57 templates/ipam/ipaddress.html:44 +#: templates/ipam/iprange.html:53 templates/ipam/prefix.html:78 +#: templates/ipam/role.html:20 templates/ipam/vlan.html:55 +#: templates/virtualization/virtualmachine.html:26 +#: templates/wireless/inc/wirelesslink_interface.html:20 +#: tenancy/forms/bulk_edit.py:141 tenancy/forms/filtersets.py:108 +#: tenancy/forms/model_forms.py:142 tenancy/tables/contacts.py:102 +#: virtualization/forms/bulk_edit.py:142 +#: virtualization/forms/bulk_import.py:105 +#: virtualization/forms/filtersets.py:150 +#: virtualization/forms/model_forms.py:197 +#: virtualization/tables/virtualmachines.py:63 +msgid "Role" +msgstr "" + +#: dcim/forms/bulk_edit.py:273 dcim/forms/bulk_edit.py:605 +#: dcim/forms/bulk_edit.py:654 templates/dcim/device.html:123 +#: templates/dcim/module.html:75 templates/dcim/modulebay.html:69 +#: templates/dcim/rack.html:65 +msgid "Serial Number" +msgstr "" + +#: dcim/forms/bulk_edit.py:276 dcim/forms/filtersets.py:303 +#: dcim/forms/filtersets.py:733 dcim/forms/filtersets.py:873 +#: dcim/forms/filtersets.py:1421 +msgid "Asset tag" +msgstr "" + +#: dcim/forms/bulk_edit.py:286 dcim/forms/bulk_import.py:212 +#: dcim/forms/filtersets.py:288 templates/dcim/rack.html:98 +#: templates/dcim/rack_edit.html:48 +msgid "Width" +msgstr "" + +#: dcim/forms/bulk_edit.py:292 +msgid "Height (U)" +msgstr "" + +#: dcim/forms/bulk_edit.py:297 +msgid "Descending units" +msgstr "" + +#: dcim/forms/bulk_edit.py:300 +msgid "Outer width" +msgstr "" + +#: dcim/forms/bulk_edit.py:305 +msgid "Outer depth" +msgstr "" + +#: dcim/forms/bulk_edit.py:310 dcim/forms/bulk_import.py:217 +msgid "Outer unit" +msgstr "" + +#: dcim/forms/bulk_edit.py:315 +msgid "Mounting depth" +msgstr "" + +#: dcim/forms/bulk_edit.py:320 dcim/forms/bulk_edit.py:349 +#: dcim/forms/bulk_edit.py:434 dcim/forms/bulk_edit.py:457 +#: dcim/forms/bulk_edit.py:473 dcim/forms/bulk_edit.py:493 +#: dcim/forms/bulk_import.py:324 dcim/forms/bulk_import.py:350 +#: dcim/forms/filtersets.py:248 dcim/forms/filtersets.py:308 +#: dcim/forms/filtersets.py:332 dcim/forms/filtersets.py:420 +#: dcim/forms/filtersets.py:525 dcim/forms/filtersets.py:544 +#: dcim/forms/filtersets.py:600 dcim/forms/model_forms.py:337 +#: dcim/tables/devicetypes.py:103 dcim/tables/modules.py:35 +#: dcim/tables/racks.py:103 extras/forms/bulk_edit.py:44 +#: extras/forms/bulk_edit.py:102 extras/forms/bulk_edit.py:152 +#: extras/forms/bulk_edit.py:256 extras/forms/filtersets.py:62 +#: extras/forms/filtersets.py:130 extras/forms/filtersets.py:217 +#: ipam/forms/bulk_edit.py:189 templates/dcim/device.html:346 +#: templates/dcim/devicetype.html:52 templates/dcim/moduletype.html:31 +#: templates/dcim/rack_edit.html:60 templates/dcim/rack_edit.html:63 +#: templates/extras/configcontext.html:18 templates/extras/customlink.html:26 +#: templates/extras/savedfilter.html:34 templates/ipam/role.html:33 +msgid "Weight" +msgstr "" + +#: dcim/forms/bulk_edit.py:325 dcim/forms/filtersets.py:313 +msgid "Max weight" +msgstr "" + +#: dcim/forms/bulk_edit.py:330 dcim/forms/bulk_edit.py:439 +#: dcim/forms/bulk_edit.py:478 dcim/forms/bulk_import.py:223 +#: dcim/forms/bulk_import.py:329 dcim/forms/bulk_import.py:355 +#: dcim/forms/filtersets.py:318 dcim/forms/filtersets.py:529 +#: dcim/forms/filtersets.py:604 +msgid "Weight unit" +msgstr "" + +#: dcim/forms/bulk_edit.py:344 dcim/forms/bulk_edit.py:800 +#: dcim/forms/bulk_import.py:262 dcim/forms/bulk_import.py:265 +#: dcim/forms/bulk_import.py:490 dcim/forms/bulk_import.py:1286 +#: dcim/forms/bulk_import.py:1290 dcim/forms/filtersets.py:100 +#: dcim/forms/filtersets.py:336 dcim/forms/filtersets.py:350 +#: dcim/forms/filtersets.py:388 dcim/forms/filtersets.py:692 +#: dcim/forms/filtersets.py:941 dcim/forms/filtersets.py:1072 +#: dcim/forms/model_forms.py:241 dcim/forms/model_forms.py:413 +#: dcim/forms/model_forms.py:661 dcim/forms/object_create.py:366 +#: dcim/tables/devices.py:194 dcim/tables/power.py:70 dcim/tables/racks.py:148 +#: ipam/forms/bulk_edit.py:466 ipam/forms/filtersets.py:430 +#: ipam/forms/model_forms.py:573 templates/dcim/device.html:47 +#: templates/dcim/inc/cable_termination.html:16 +#: templates/dcim/powerfeed.html:31 templates/dcim/rack.html:13 +#: templates/dcim/rack/base.html:4 templates/dcim/rack_edit.html:8 +#: templates/dcim/rackreservation.html:19 +#: templates/dcim/rackreservation.html:38 +#: virtualization/forms/model_forms.py:115 +msgid "Rack" +msgstr "" + +#: dcim/forms/bulk_edit.py:346 dcim/forms/bulk_edit.py:623 +#: dcim/forms/filtersets.py:245 dcim/forms/filtersets.py:329 +#: dcim/forms/filtersets.py:414 dcim/forms/filtersets.py:539 +#: dcim/forms/filtersets.py:646 dcim/forms/filtersets.py:846 +#: dcim/forms/model_forms.py:588 dcim/forms/model_forms.py:1373 +#: templates/dcim/device_edit.html:20 templates/dcim/inventoryitem_edit.html:23 +msgid "Hardware" +msgstr "" + +#: dcim/forms/bulk_edit.py:400 dcim/forms/bulk_edit.py:464 +#: dcim/forms/bulk_edit.py:528 dcim/forms/bulk_edit.py:552 +#: dcim/forms/bulk_edit.py:633 dcim/forms/bulk_edit.py:1157 +#: dcim/forms/bulk_edit.py:1544 dcim/forms/bulk_import.py:311 +#: dcim/forms/bulk_import.py:345 dcim/forms/bulk_import.py:387 +#: dcim/forms/bulk_import.py:423 dcim/forms/bulk_import.py:1015 +#: dcim/forms/filtersets.py:425 dcim/forms/filtersets.py:549 +#: dcim/forms/filtersets.py:625 dcim/forms/filtersets.py:702 +#: dcim/forms/filtersets.py:851 dcim/forms/filtersets.py:1414 +#: dcim/forms/model_forms.py:274 dcim/forms/model_forms.py:288 +#: dcim/forms/model_forms.py:330 dcim/forms/model_forms.py:370 +#: dcim/forms/model_forms.py:967 dcim/forms/model_forms.py:1308 +#: dcim/forms/object_import.py:192 dcim/tables/devices.py:129 +#: dcim/tables/devices.py:205 dcim/tables/devices.py:925 +#: dcim/tables/devicetypes.py:81 dcim/tables/devicetypes.py:304 +#: dcim/tables/modules.py:20 dcim/tables/modules.py:60 +#: templates/dcim/devicetype.html:17 templates/dcim/inventoryitem.html:45 +#: templates/dcim/manufacturer.html:34 templates/dcim/modulebay.html:61 +#: templates/dcim/moduletype.html:15 templates/dcim/platform.html:40 +msgid "Manufacturer" +msgstr "" + +#: dcim/forms/bulk_edit.py:405 dcim/forms/bulk_import.py:317 +#: dcim/forms/filtersets.py:430 dcim/forms/model_forms.py:292 +msgid "Default platform" +msgstr "" + +#: dcim/forms/bulk_edit.py:410 dcim/forms/bulk_edit.py:469 +#: dcim/forms/filtersets.py:433 dcim/forms/filtersets.py:553 +msgid "Part number" +msgstr "" + +#: dcim/forms/bulk_edit.py:414 +msgid "U height" +msgstr "" + +#: dcim/forms/bulk_edit.py:426 +msgid "Exclude from utilization" +msgstr "" + +#: dcim/forms/bulk_edit.py:429 dcim/forms/bulk_edit.py:598 +#: dcim/forms/bulk_import.py:517 dcim/forms/filtersets.py:442 +#: dcim/forms/filtersets.py:724 templates/dcim/device.html:117 +#: templates/dcim/devicetype.html:68 +msgid "Airflow" +msgstr "" + +#: dcim/forms/bulk_edit.py:453 dcim/forms/model_forms.py:303 +#: dcim/tables/devicetypes.py:78 templates/dcim/device.html:107 +#: templates/dcim/devicebay.html:59 templates/dcim/module.html:59 +msgid "Device Type" +msgstr "" + +#: dcim/forms/bulk_edit.py:492 dcim/forms/model_forms.py:336 +#: dcim/tables/modules.py:17 dcim/tables/modules.py:65 +#: templates/dcim/module.html:63 templates/dcim/modulebay.html:65 +#: templates/dcim/moduletype.html:11 +msgid "Module Type" +msgstr "" + +#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:472 +msgid "VM role" +msgstr "" + +#: dcim/forms/bulk_edit.py:509 dcim/forms/bulk_edit.py:533 +#: dcim/forms/bulk_edit.py:613 dcim/forms/bulk_import.py:368 +#: dcim/forms/bulk_import.py:372 dcim/forms/bulk_import.py:394 +#: dcim/forms/bulk_import.py:398 dcim/forms/bulk_import.py:523 +#: dcim/forms/bulk_import.py:527 dcim/forms/filtersets.py:615 +#: dcim/forms/filtersets.py:630 dcim/forms/filtersets.py:743 +#: dcim/forms/model_forms.py:349 dcim/forms/model_forms.py:375 +#: dcim/forms/model_forms.py:476 virtualization/forms/bulk_import.py:131 +#: virtualization/forms/bulk_import.py:132 +#: virtualization/forms/filtersets.py:177 +#: virtualization/forms/model_forms.py:216 +msgid "Config template" +msgstr "" + +#: dcim/forms/bulk_edit.py:557 dcim/forms/bulk_edit.py:951 +#: dcim/forms/bulk_import.py:429 dcim/forms/filtersets.py:110 +#: dcim/forms/model_forms.py:435 dcim/forms/model_forms.py:775 +#: dcim/forms/model_forms.py:789 extras/filtersets.py:421 +msgid "Device type" +msgstr "" + +#: dcim/forms/bulk_edit.py:565 dcim/forms/bulk_import.py:410 +#: dcim/forms/filtersets.py:115 dcim/forms/model_forms.py:440 +msgid "Device role" +msgstr "" + +#: dcim/forms/bulk_edit.py:588 dcim/forms/bulk_import.py:435 +#: dcim/forms/filtersets.py:716 dcim/forms/model_forms.py:385 +#: dcim/forms/model_forms.py:444 extras/filtersets.py:437 +#: templates/dcim/device.html:208 templates/dcim/platform.html:27 +#: templates/virtualization/virtualmachine.html:30 +#: virtualization/forms/bulk_edit.py:157 +#: virtualization/forms/bulk_import.py:121 +#: virtualization/forms/filtersets.py:161 +#: virtualization/forms/model_forms.py:205 +msgid "Platform" +msgstr "" + +#: dcim/forms/bulk_edit.py:621 dcim/forms/bulk_edit.py:1171 +#: dcim/forms/bulk_edit.py:1534 dcim/forms/bulk_edit.py:1580 +#: dcim/forms/bulk_import.py:578 dcim/forms/bulk_import.py:640 +#: dcim/forms/bulk_import.py:666 dcim/forms/bulk_import.py:692 +#: dcim/forms/bulk_import.py:712 dcim/forms/bulk_import.py:765 +#: dcim/forms/bulk_import.py:879 dcim/forms/bulk_import.py:927 +#: dcim/forms/bulk_import.py:944 dcim/forms/bulk_import.py:956 +#: dcim/forms/bulk_import.py:1004 dcim/forms/bulk_import.py:1350 +#: dcim/forms/connections.py:23 dcim/forms/filtersets.py:127 +#: dcim/forms/filtersets.py:824 dcim/forms/filtersets.py:957 +#: dcim/forms/filtersets.py:1146 dcim/forms/filtersets.py:1168 +#: dcim/forms/filtersets.py:1190 dcim/forms/filtersets.py:1207 +#: dcim/forms/filtersets.py:1227 dcim/forms/filtersets.py:1334 +#: dcim/forms/filtersets.py:1356 dcim/forms/filtersets.py:1377 +#: dcim/forms/filtersets.py:1392 dcim/forms/filtersets.py:1403 +#: dcim/forms/filtersets.py:1467 dcim/forms/filtersets.py:1491 +#: dcim/forms/filtersets.py:1515 dcim/forms/model_forms.py:554 +#: dcim/forms/model_forms.py:752 dcim/forms/model_forms.py:1003 +#: dcim/forms/model_forms.py:1452 dcim/forms/object_create.py:239 +#: dcim/tables/connections.py:22 dcim/tables/connections.py:41 +#: dcim/tables/connections.py:60 dcim/tables/devices.py:314 +#: dcim/tables/devices.py:374 dcim/tables/devices.py:418 +#: dcim/tables/devices.py:463 dcim/tables/devices.py:511 +#: dcim/tables/devices.py:597 dcim/tables/devices.py:693 +#: dcim/tables/devices.py:753 dcim/tables/devices.py:803 +#: dcim/tables/devices.py:863 dcim/tables/devices.py:915 +#: dcim/tables/devices.py:1037 dcim/tables/modules.py:52 +#: extras/forms/filtersets.py:304 ipam/forms/bulk_import.py:306 +#: ipam/forms/bulk_import.py:492 ipam/forms/bulk_import.py:543 +#: ipam/forms/filtersets.py:594 ipam/forms/model_forms.py:687 +#: ipam/tables/vlans.py:176 templates/dcim/consoleport.html:23 +#: templates/dcim/consoleserverport.html:23 templates/dcim/device.html:13 +#: templates/dcim/device.html:145 templates/dcim/device_edit.html:10 +#: templates/dcim/devicebay.html:23 templates/dcim/devicebay.html:55 +#: templates/dcim/frontport.html:23 templates/dcim/interface.html:31 +#: templates/dcim/interface.html:163 templates/dcim/inventoryitem.html:21 +#: templates/dcim/module.html:55 templates/dcim/modulebay.html:21 +#: templates/dcim/poweroutlet.html:23 templates/dcim/powerport.html:23 +#: templates/dcim/rearport.html:23 templates/dcim/virtualchassis.html:58 +#: templates/dcim/virtualchassis_edit.html:52 +#: templates/dcim/virtualdevicecontext.html:25 +#: templates/ipam/ipaddress_edit.html:42 +#: templates/ipam/l2vpntermination_edit.html:22 +#: templates/ipam/service_create.html:17 templates/ipam/service_edit.html:16 +#: templates/virtualization/virtualmachine.html:115 +#: templates/wireless/inc/wirelesslink_interface.html:6 +#: virtualization/filtersets.py:163 virtualization/forms/bulk_edit.py:134 +#: virtualization/forms/bulk_import.py:98 +#: virtualization/forms/filtersets.py:121 +#: virtualization/forms/model_forms.py:187 +#: virtualization/tables/virtualmachines.py:59 +#: wireless/forms/model_forms.py:100 wireless/forms/model_forms.py:140 +#: wireless/tables/wirelesslan.py:75 +msgid "Device" +msgstr "" + +#: dcim/forms/bulk_edit.py:624 netbox/navigation/menu.py:421 +#: templates/extras/dashboard/widget_config.html:7 +msgid "Configuration" +msgstr "" + +#: dcim/forms/bulk_edit.py:638 dcim/forms/bulk_import.py:590 +#: dcim/forms/model_forms.py:568 dcim/forms/model_forms.py:794 +msgid "Module type" +msgstr "" + +#: dcim/forms/bulk_edit.py:689 dcim/forms/bulk_edit.py:874 +#: dcim/forms/bulk_edit.py:893 dcim/forms/bulk_edit.py:916 +#: dcim/forms/bulk_edit.py:958 dcim/forms/bulk_edit.py:1002 +#: dcim/forms/bulk_edit.py:1053 dcim/forms/bulk_edit.py:1080 +#: dcim/forms/bulk_edit.py:1107 dcim/forms/bulk_edit.py:1125 +#: dcim/forms/bulk_edit.py:1143 dcim/forms/filtersets.py:63 +#: dcim/forms/object_create.py:45 templates/dcim/cable.html:33 +#: templates/dcim/consoleport.html:35 templates/dcim/consoleserverport.html:35 +#: templates/dcim/devicebay.html:31 templates/dcim/frontport.html:35 +#: templates/dcim/inc/panels/inventory_items.html:11 +#: templates/dcim/interface.html:43 templates/dcim/inventoryitem.html:33 +#: templates/dcim/modulebay.html:31 templates/dcim/poweroutlet.html:35 +#: templates/dcim/powerport.html:35 templates/dcim/rearport.html:35 +#: templates/extras/customfield.html:27 templates/generic/bulk_import.html:155 +msgid "Label" +msgstr "" + +#: dcim/forms/bulk_edit.py:698 dcim/forms/filtersets.py:974 +#: templates/dcim/cable.html:51 +msgid "Length" +msgstr "" + +#: dcim/forms/bulk_edit.py:703 dcim/forms/bulk_import.py:1158 +#: dcim/forms/bulk_import.py:1161 dcim/forms/filtersets.py:978 +msgid "Length unit" +msgstr "" + +#: dcim/forms/bulk_edit.py:727 templates/dcim/virtualchassis.html:24 +msgid "Domain" +msgstr "" + +#: dcim/forms/bulk_edit.py:795 dcim/forms/bulk_import.py:1273 +#: dcim/forms/filtersets.py:1063 dcim/forms/model_forms.py:656 +msgid "Power panel" +msgstr "" + +#: dcim/forms/bulk_edit.py:817 dcim/forms/bulk_import.py:1309 +#: dcim/forms/filtersets.py:1085 templates/dcim/powerfeed.html:90 +msgid "Supply" +msgstr "" + +#: dcim/forms/bulk_edit.py:823 dcim/forms/bulk_import.py:1314 +#: dcim/forms/filtersets.py:1090 templates/dcim/powerfeed.html:102 +msgid "Phase" +msgstr "" + +#: dcim/forms/bulk_edit.py:829 dcim/forms/filtersets.py:1095 +#: templates/dcim/powerfeed.html:94 +msgid "Voltage" +msgstr "" + +#: dcim/forms/bulk_edit.py:833 dcim/forms/filtersets.py:1099 +#: templates/dcim/powerfeed.html:98 +msgid "Amperage" +msgstr "" + +#: dcim/forms/bulk_edit.py:837 dcim/forms/filtersets.py:1103 +msgid "Max utilization" +msgstr "" + +#: dcim/forms/bulk_edit.py:841 dcim/forms/bulk_edit.py:1200 +#: dcim/forms/bulk_edit.py:1217 dcim/forms/bulk_edit.py:1234 +#: dcim/forms/bulk_edit.py:1252 dcim/forms/bulk_edit.py:1340 +#: dcim/forms/bulk_edit.py:1478 dcim/forms/bulk_edit.py:1495 +msgid "Mark connected" +msgstr "" + +#: dcim/forms/bulk_edit.py:926 +msgid "Maximum draw" +msgstr "" + +#: dcim/forms/bulk_edit.py:929 dcim/models/device_component_templates.py:257 +#: dcim/models/device_components.py:358 +msgid "Maximum power draw (watts)" +msgstr "" + +#: dcim/forms/bulk_edit.py:932 +msgid "Allocated draw" +msgstr "" + +#: dcim/forms/bulk_edit.py:935 dcim/models/device_component_templates.py:264 +#: dcim/models/device_components.py:365 +msgid "Allocated power draw (watts)" +msgstr "" + +#: dcim/forms/bulk_edit.py:968 dcim/forms/bulk_import.py:723 +#: dcim/forms/model_forms.py:847 dcim/forms/model_forms.py:1075 +#: dcim/forms/model_forms.py:1360 dcim/forms/object_import.py:60 +msgid "Power port" +msgstr "" + +#: dcim/forms/bulk_edit.py:973 +msgid "Feed leg" +msgstr "" + +#: dcim/forms/bulk_edit.py:1019 dcim/forms/bulk_edit.py:1325 +msgid "Management only" +msgstr "" + +#: dcim/forms/bulk_edit.py:1029 dcim/forms/bulk_edit.py:1331 +#: dcim/forms/bulk_import.py:813 dcim/forms/filtersets.py:1285 +#: dcim/forms/object_import.py:95 dcim/models/device_component_templates.py:412 +#: dcim/models/device_components.py:668 +msgid "PoE mode" +msgstr "" + +#: dcim/forms/bulk_edit.py:1035 dcim/forms/bulk_edit.py:1337 +#: dcim/forms/bulk_import.py:819 dcim/forms/filtersets.py:1290 +#: dcim/forms/object_import.py:100 +#: dcim/models/device_component_templates.py:418 +#: dcim/models/device_components.py:674 +msgid "PoE type" +msgstr "" + +#: dcim/forms/bulk_edit.py:1041 dcim/forms/filtersets.py:1295 +#: dcim/forms/object_import.py:105 +msgid "Wireless role" +msgstr "" + +#: dcim/forms/bulk_edit.py:1178 dcim/forms/model_forms.py:587 +#: dcim/forms/model_forms.py:1018 dcim/tables/devices.py:337 +#: templates/dcim/consoleport.html:27 templates/dcim/consoleserverport.html:27 +#: templates/dcim/frontport.html:27 templates/dcim/interface.html:35 +#: templates/dcim/module.html:51 templates/dcim/modulebay.html:57 +#: templates/dcim/poweroutlet.html:27 templates/dcim/powerport.html:27 +#: templates/dcim/rearport.html:27 +msgid "Module" +msgstr "" + +#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:663 +#: templates/dcim/interface.html:105 +msgid "LAG" +msgstr "" + +#: dcim/forms/bulk_edit.py:1310 dcim/forms/model_forms.py:1102 +msgid "Virtual device contexts" +msgstr "" + +#: dcim/forms/bulk_edit.py:1316 dcim/forms/bulk_import.py:651 +#: dcim/forms/bulk_import.py:677 dcim/forms/filtersets.py:1155 +#: dcim/forms/filtersets.py:1177 dcim/forms/filtersets.py:1249 +#: dcim/tables/devices.py:609 +#: templates/circuits/inc/circuit_termination.html:94 +#: templates/dcim/consoleport.html:43 templates/dcim/consoleserverport.html:43 +msgid "Speed" +msgstr "" + +#: dcim/forms/bulk_edit.py:1345 dcim/forms/bulk_import.py:822 +#: virtualization/forms/bulk_edit.py:230 +#: virtualization/forms/bulk_import.py:164 +msgid "Mode" +msgstr "" + +#: dcim/forms/bulk_edit.py:1353 dcim/forms/model_forms.py:1151 +#: ipam/forms/bulk_import.py:180 ipam/forms/filtersets.py:481 +#: ipam/models/vlans.py:82 virtualization/forms/bulk_edit.py:237 +#: virtualization/forms/model_forms.py:303 +msgid "VLAN group" +msgstr "" + +#: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1156 +#: dcim/tables/devices.py:582 virtualization/forms/bulk_edit.py:245 +#: virtualization/forms/model_forms.py:308 +msgid "Untagged VLAN" +msgstr "" + +#: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1165 +#: dcim/tables/devices.py:588 virtualization/forms/bulk_edit.py:253 +#: virtualization/forms/model_forms.py:317 +msgid "Tagged VLANs" +msgstr "" + +#: dcim/forms/bulk_edit.py:1379 dcim/forms/model_forms.py:1138 +msgid "Wireless LAN group" +msgstr "" + +#: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1143 +#: dcim/tables/devices.py:618 netbox/navigation/menu.py:134 +#: templates/dcim/interface.html:285 wireless/tables/wirelesslan.py:24 +msgid "Wireless LANs" +msgstr "" + +#: dcim/forms/bulk_edit.py:1393 dcim/forms/filtersets.py:1223 +#: dcim/forms/model_forms.py:1184 ipam/forms/bulk_edit.py:272 +#: ipam/forms/bulk_edit.py:363 ipam/forms/filtersets.py:170 +#: templates/dcim/interface.html:122 templates/ipam/prefix.html:96 +#: virtualization/forms/model_forms.py:331 +msgid "Addressing" +msgstr "" + +#: dcim/forms/bulk_edit.py:1394 dcim/forms/filtersets.py:645 +#: dcim/forms/model_forms.py:1185 virtualization/forms/model_forms.py:332 +msgid "Operation" +msgstr "" + +#: dcim/forms/bulk_edit.py:1395 dcim/forms/filtersets.py:1224 +#: dcim/forms/model_forms.py:879 dcim/forms/model_forms.py:1187 +msgid "PoE" +msgstr "" + +#: dcim/forms/bulk_edit.py:1396 dcim/forms/model_forms.py:1186 +#: templates/dcim/interface.html:93 virtualization/forms/bulk_edit.py:264 +#: virtualization/forms/model_forms.py:333 +msgid "Related Interfaces" +msgstr "" + +#: dcim/forms/bulk_edit.py:1397 dcim/forms/model_forms.py:1188 +#: virtualization/forms/bulk_edit.py:265 +#: virtualization/forms/model_forms.py:334 +msgid "802.1Q Switching" +msgstr "" + +#: dcim/forms/bulk_edit.py:1458 dcim/forms/bulk_edit.py:1460 +msgid "Interface mode must be specified to assign VLANs" +msgstr "" + +#: dcim/forms/bulk_edit.py:1465 dcim/forms/common.py:50 +msgid "An access interface cannot have tagged VLANs assigned." +msgstr "" + +#: dcim/forms/bulk_import.py:63 +msgid "Name of parent region" +msgstr "" + +#: dcim/forms/bulk_import.py:77 +msgid "Name of parent site group" +msgstr "" + +#: dcim/forms/bulk_import.py:96 +msgid "Assigned region" +msgstr "" + +#: dcim/forms/bulk_import.py:103 tenancy/forms/bulk_import.py:44 +#: tenancy/forms/bulk_import.py:85 wireless/forms/bulk_import.py:40 +msgid "Assigned group" +msgstr "" + +#: dcim/forms/bulk_import.py:122 +msgid "available options" +msgstr "" + +#: dcim/forms/bulk_import.py:133 dcim/forms/bulk_import.py:480 +#: dcim/forms/bulk_import.py:1270 ipam/forms/bulk_import.py:177 +#: ipam/forms/bulk_import.py:444 virtualization/forms/bulk_import.py:62 +#: virtualization/forms/bulk_import.py:88 +msgid "Assigned site" +msgstr "" + +#: dcim/forms/bulk_import.py:140 +msgid "Parent location" +msgstr "" + +#: dcim/forms/bulk_import.py:142 +msgid "Location not found." +msgstr "" + +#: dcim/forms/bulk_import.py:191 +msgid "Name of assigned tenant" +msgstr "" + +#: dcim/forms/bulk_import.py:203 +msgid "Name of assigned role" +msgstr "" + +#: dcim/forms/bulk_import.py:209 +msgid "Rack type" +msgstr "" + +#: dcim/forms/bulk_import.py:214 +msgid "Rail-to-rail width (in inches)" +msgstr "" + +#: dcim/forms/bulk_import.py:220 +msgid "Unit for outer dimensions" +msgstr "" + +#: dcim/forms/bulk_import.py:226 +msgid "Unit for rack weights" +msgstr "" + +#: dcim/forms/bulk_import.py:252 +msgid "Parent site" +msgstr "" + +#: dcim/forms/bulk_import.py:259 dcim/forms/bulk_import.py:1283 +msgid "Rack's location (if any)" +msgstr "" + +#: dcim/forms/bulk_import.py:268 dcim/forms/model_forms.py:246 +#: dcim/tables/racks.py:153 templates/dcim/rackreservation.html:11 +#: templates/dcim/rackreservation.html:52 +msgid "Units" +msgstr "" + +#: dcim/forms/bulk_import.py:271 +msgid "Comma-separated list of individual unit numbers" +msgstr "" + +#: dcim/forms/bulk_import.py:314 +msgid "The manufacturer which produces this device type" +msgstr "" + +#: dcim/forms/bulk_import.py:321 +msgid "The default platform for devices of this type (optional)" +msgstr "" + +#: dcim/forms/bulk_import.py:326 +msgid "Device weight" +msgstr "" + +#: dcim/forms/bulk_import.py:332 +msgid "Unit for device weight" +msgstr "" + +#: dcim/forms/bulk_import.py:352 +msgid "Module weight" +msgstr "" + +#: dcim/forms/bulk_import.py:358 +msgid "Unit for module weight" +msgstr "" + +#: dcim/forms/bulk_import.py:391 +msgid "Limit platform assignments to this manufacturer" +msgstr "" + +#: dcim/forms/bulk_import.py:413 tenancy/forms/bulk_import.py:106 +msgid "Assigned role" +msgstr "" + +#: dcim/forms/bulk_import.py:426 +msgid "Device type manufacturer" +msgstr "" + +#: dcim/forms/bulk_import.py:432 +msgid "Device type model" +msgstr "" + +#: dcim/forms/bulk_import.py:439 virtualization/forms/bulk_import.py:125 +msgid "Assigned platform" +msgstr "" + +#: dcim/forms/bulk_import.py:447 dcim/forms/bulk_import.py:451 +#: dcim/forms/model_forms.py:460 +msgid "Virtual chassis" +msgstr "" + +#: dcim/forms/bulk_import.py:454 dcim/forms/model_forms.py:449 +#: dcim/tables/devices.py:231 extras/filtersets.py:470 +#: extras/forms/filtersets.py:305 ipam/forms/bulk_edit.py:480 +#: ipam/forms/model_forms.py:590 templates/dcim/device.html:256 +#: templates/virtualization/cluster.html:11 +#: templates/virtualization/virtualmachine.html:92 +#: templates/virtualization/virtualmachine.html:102 +#: virtualization/filtersets.py:153 virtualization/filtersets.py:268 +#: virtualization/forms/bulk_edit.py:126 virtualization/forms/bulk_import.py:91 +#: virtualization/forms/filtersets.py:95 virtualization/forms/filtersets.py:116 +#: virtualization/forms/filtersets.py:192 +#: virtualization/forms/model_forms.py:81 +#: virtualization/forms/model_forms.py:178 +#: virtualization/tables/virtualmachines.py:55 +msgid "Cluster" +msgstr "" + +#: dcim/forms/bulk_import.py:458 +msgid "Virtualization cluster" +msgstr "" + +#: dcim/forms/bulk_import.py:487 +msgid "Assigned location (if any)" +msgstr "" + +#: dcim/forms/bulk_import.py:494 +msgid "Assigned rack (if any)" +msgstr "" + +#: dcim/forms/bulk_import.py:497 +msgid "Face" +msgstr "" + +#: dcim/forms/bulk_import.py:500 +msgid "Mounted rack face" +msgstr "" + +#: dcim/forms/bulk_import.py:507 +msgid "Parent device (for child devices)" +msgstr "" + +#: dcim/forms/bulk_import.py:510 +msgid "Device bay" +msgstr "" + +#: dcim/forms/bulk_import.py:514 +msgid "Device bay in which this device is installed (for child devices)" +msgstr "" + +#: dcim/forms/bulk_import.py:520 +msgid "Airflow direction" +msgstr "" + +#: dcim/forms/bulk_import.py:581 +msgid "The device in which this module is installed" +msgstr "" + +#: dcim/forms/bulk_import.py:584 dcim/forms/model_forms.py:561 +msgid "Module bay" +msgstr "" + +#: dcim/forms/bulk_import.py:587 +msgid "The module bay in which this module is installed" +msgstr "" + +#: dcim/forms/bulk_import.py:593 +msgid "The type of module" +msgstr "" + +#: dcim/forms/bulk_import.py:601 dcim/forms/model_forms.py:574 +msgid "Replicate components" +msgstr "" + +#: dcim/forms/bulk_import.py:603 +msgid "" +"Automatically populate components associated with this module type (enabled " +"by default)" +msgstr "" + +#: dcim/forms/bulk_import.py:606 dcim/forms/model_forms.py:580 +msgid "Adopt components" +msgstr "" + +#: dcim/forms/bulk_import.py:608 dcim/forms/model_forms.py:583 +msgid "Adopt already existing components" +msgstr "" + +#: dcim/forms/bulk_import.py:648 dcim/forms/bulk_import.py:674 +#: dcim/forms/bulk_import.py:700 +msgid "Port type" +msgstr "" + +#: dcim/forms/bulk_import.py:656 dcim/forms/bulk_import.py:682 +msgid "Port speed in bps" +msgstr "" + +#: dcim/forms/bulk_import.py:720 +msgid "Outlet type" +msgstr "" + +#: dcim/forms/bulk_import.py:727 +msgid "Local power port which feeds this outlet" +msgstr "" + +#: dcim/forms/bulk_import.py:730 +msgid "Feed lag" +msgstr "" + +#: dcim/forms/bulk_import.py:733 +msgid "Electrical phase (for three-phase circuits)" +msgstr "" + +#: dcim/forms/bulk_import.py:774 dcim/forms/model_forms.py:1113 +#: virtualization/forms/bulk_import.py:154 +#: virtualization/forms/model_forms.py:287 +msgid "Parent interface" +msgstr "" + +#: dcim/forms/bulk_import.py:781 dcim/forms/model_forms.py:1121 +#: virtualization/forms/bulk_import.py:161 +#: virtualization/forms/model_forms.py:295 +msgid "Bridged interface" +msgstr "" + +#: dcim/forms/bulk_import.py:784 +msgid "Lag" +msgstr "" + +#: dcim/forms/bulk_import.py:788 +msgid "Parent LAG interface" +msgstr "" + +#: dcim/forms/bulk_import.py:791 +msgid "Vdcs" +msgstr "" + +#: dcim/forms/bulk_import.py:796 +msgid "VDC names separated by commas, encased with double quotes. Example:" +msgstr "" + +#: dcim/forms/bulk_import.py:802 +msgid "Physical medium" +msgstr "" + +#: dcim/forms/bulk_import.py:805 dcim/forms/filtersets.py:1256 +msgid "Duplex" +msgstr "" + +#: dcim/forms/bulk_import.py:810 +msgid "Poe mode" +msgstr "" + +#: dcim/forms/bulk_import.py:816 +msgid "Poe type" +msgstr "" + +#: dcim/forms/bulk_import.py:825 virtualization/forms/bulk_import.py:167 +msgid "IEEE 802.1Q operational mode (for L2 interfaces)" +msgstr "" + +#: dcim/forms/bulk_import.py:832 ipam/forms/bulk_import.py:163 +#: ipam/forms/bulk_import.py:249 ipam/forms/bulk_import.py:285 +#: ipam/forms/filtersets.py:200 ipam/forms/filtersets.py:270 +#: ipam/forms/filtersets.py:325 virtualization/forms/bulk_import.py:174 +msgid "Assigned VRF" +msgstr "" + +#: dcim/forms/bulk_import.py:835 +msgid "Rf role" +msgstr "" + +#: dcim/forms/bulk_import.py:838 +msgid "Wireless role (AP/station)" +msgstr "" + +#: dcim/forms/bulk_import.py:884 dcim/forms/model_forms.py:892 +#: dcim/forms/model_forms.py:1368 dcim/forms/object_import.py:122 +msgid "Rear port" +msgstr "" + +#: dcim/forms/bulk_import.py:887 +msgid "Corresponding rear port" +msgstr "" + +#: dcim/forms/bulk_import.py:892 dcim/forms/bulk_import.py:933 +#: dcim/forms/bulk_import.py:1148 +msgid "Physical medium classification" +msgstr "" + +#: dcim/forms/bulk_import.py:961 dcim/tables/devices.py:824 +msgid "Installed device" +msgstr "" + +#: dcim/forms/bulk_import.py:965 +msgid "Child device installed within this bay" +msgstr "" + +#: dcim/forms/bulk_import.py:967 +msgid "Child device not found." +msgstr "" + +#: dcim/forms/bulk_import.py:1025 +msgid "Parent inventory item" +msgstr "" + +#: dcim/forms/bulk_import.py:1028 +msgid "Component type" +msgstr "" + +#: dcim/forms/bulk_import.py:1032 +msgid "Component Type" +msgstr "" + +#: dcim/forms/bulk_import.py:1035 +msgid "Compnent name" +msgstr "" + +#: dcim/forms/bulk_import.py:1037 +msgid "Component Name" +msgstr "" + +#: dcim/forms/bulk_import.py:1103 +msgid "Side A device" +msgstr "" + +#: dcim/forms/bulk_import.py:1106 dcim/forms/bulk_import.py:1124 +msgid "Device name" +msgstr "" + +#: dcim/forms/bulk_import.py:1109 +msgid "Side A type" +msgstr "" + +#: dcim/forms/bulk_import.py:1112 dcim/forms/bulk_import.py:1130 +msgid "Termination type" +msgstr "" + +#: dcim/forms/bulk_import.py:1115 +msgid "Side A name" +msgstr "" + +#: dcim/forms/bulk_import.py:1116 dcim/forms/bulk_import.py:1134 +msgid "Termination name" +msgstr "" + +#: dcim/forms/bulk_import.py:1121 +msgid "Side B device" +msgstr "" + +#: dcim/forms/bulk_import.py:1127 +msgid "Side B type" +msgstr "" + +#: dcim/forms/bulk_import.py:1133 +msgid "Side B name" +msgstr "" + +#: dcim/forms/bulk_import.py:1142 wireless/forms/bulk_import.py:86 +msgid "Connection status" +msgstr "" + +#: dcim/forms/bulk_import.py:1221 dcim/forms/model_forms.py:688 +#: dcim/tables/devices.py:1007 templates/dcim/device.html:147 +#: templates/dcim/virtualchassis.html:28 templates/dcim/virtualchassis.html:60 +msgid "Master" +msgstr "" + +#: dcim/forms/bulk_import.py:1225 +msgid "Master device" +msgstr "" + +#: dcim/forms/bulk_import.py:1242 +msgid "Name of parent site" +msgstr "" + +#: dcim/forms/bulk_import.py:1276 +msgid "Upstream power panel" +msgstr "" + +#: dcim/forms/bulk_import.py:1306 +msgid "Primary or redundant" +msgstr "" + +#: dcim/forms/bulk_import.py:1311 +msgid "Supply type (AC/DC)" +msgstr "" + +#: dcim/forms/bulk_import.py:1316 +msgid "Single or three-phase" +msgstr "" + +#: dcim/forms/common.py:24 dcim/models/device_components.py:529 +#: templates/dcim/interface.html:58 +#: templates/virtualization/vminterface.html:58 +#: virtualization/forms/bulk_edit.py:222 +msgid "MTU" +msgstr "" + +#: dcim/forms/common.py:65 +#, 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 "" + +#: dcim/forms/common.py:110 +msgid "" +"Cannot install module with placeholder values in a module bay with no " +"position defined." +msgstr "" + +#: dcim/forms/common.py:119 +#, python-brace-format +msgid "Cannot adopt {model} {name} as it already belongs to a module" +msgstr "" + +#: dcim/forms/common.py:128 +#, python-brace-format +msgid "A {model} named {name} already exists" +msgstr "" + +#: dcim/forms/connections.py:45 dcim/tables/power.py:66 +#: templates/dcim/inc/cable_termination.html:37 +#: templates/dcim/powerfeed.html:27 templates/dcim/powerpanel.html:19 +#: templates/dcim/trace/powerpanel.html:4 +msgid "Power Panel" +msgstr "" + +#: dcim/forms/connections.py:54 dcim/forms/model_forms.py:669 +#: templates/dcim/powerfeed.html:22 templates/dcim/powerport.html:84 +msgid "Power Feed" +msgstr "" + +#: dcim/forms/connections.py:74 +msgid "Side" +msgstr "" + +#: dcim/forms/filtersets.py:140 +msgid "Parent region" +msgstr "" + +#: dcim/forms/filtersets.py:154 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:25 +#: wireless/forms/filtersets.py:24 +msgid "Parent group" +msgstr "" + +#: dcim/forms/filtersets.py:244 dcim/forms/filtersets.py:328 +msgid "Function" +msgstr "" + +#: dcim/forms/filtersets.py:415 dcim/forms/model_forms.py:308 +#: templates/inc/panels/image_attachments.html:5 +msgid "Images" +msgstr "" + +#: dcim/forms/filtersets.py:416 dcim/forms/filtersets.py:540 +#: dcim/forms/filtersets.py:649 +msgid "Components" +msgstr "" + +#: dcim/forms/filtersets.py:437 +msgid "Subdevice role" +msgstr "" + +#: dcim/forms/filtersets.py:652 extras/forms/model_forms.py:496 +#: templates/extras/configrevision.html:171 users/forms/model_forms.py:63 +msgid "Miscellaneous" +msgstr "" + +#: dcim/forms/filtersets.py:710 +msgid "Model" +msgstr "" + +#: dcim/forms/filtersets.py:761 +msgid "Virtual chassis member" +msgstr "" + +#: dcim/forms/filtersets.py:1115 +msgid "Cabled" +msgstr "" + +#: dcim/forms/filtersets.py:1122 +msgid "Occupied" +msgstr "" + +#: dcim/forms/filtersets.py:1147 dcim/forms/filtersets.py:1169 +#: dcim/forms/filtersets.py:1191 dcim/forms/filtersets.py:1208 +#: dcim/forms/filtersets.py:1228 dcim/tables/devices.py:367 +#: templates/dcim/consoleport.html:59 templates/dcim/consoleserverport.html:59 +#: templates/dcim/frontport.html:74 templates/dcim/interface.html:142 +#: templates/dcim/powerfeed.html:118 templates/dcim/poweroutlet.html:63 +#: templates/dcim/powerport.html:63 templates/dcim/rearport.html:70 +msgid "Connection" +msgstr "" + +#: dcim/forms/filtersets.py:1236 dcim/forms/model_forms.py:1476 +#: templates/dcim/virtualdevicecontext.html:16 +msgid "Virtual Device Context" +msgstr "" + +#: dcim/forms/filtersets.py:1239 extras/forms/bulk_edit.py:294 +#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:454 +#: extras/forms/model_forms.py:445 extras/tables/tables.py:482 +#: templates/extras/journalentry.html:33 +msgid "Kind" +msgstr "" + +#: dcim/forms/filtersets.py:1268 +msgid "Mgmt only" +msgstr "" + +#: dcim/forms/filtersets.py:1280 dcim/forms/model_forms.py:1179 +#: dcim/models/device_components.py:627 templates/dcim/interface.html:130 +msgid "WWN" +msgstr "" + +#: dcim/forms/filtersets.py:1300 +msgid "Wireless channel" +msgstr "" + +#: dcim/forms/filtersets.py:1304 +msgid "Channel frequency (MHz)" +msgstr "" + +#: dcim/forms/filtersets.py:1308 +msgid "Channel width (MHz)" +msgstr "" + +#: dcim/forms/filtersets.py:1312 templates/dcim/interface.html:86 +msgid "Transmit power (dBm)" +msgstr "" + +#: dcim/forms/filtersets.py:1335 dcim/forms/filtersets.py:1357 +#: dcim/tables/devices.py:344 templates/dcim/cable.html:12 +#: templates/dcim/cable_edit.html:46 templates/dcim/cable_trace.html:43 +#: templates/dcim/frontport.html:84 +#: templates/dcim/inc/connection_endpoints.html:4 +#: templates/dcim/rearport.html:80 templates/dcim/trace/cable.html:7 +msgid "Cable" +msgstr "" + +#: dcim/forms/filtersets.py:1425 dcim/tables/devices.py:934 +msgid "Discovered" +msgstr "" + +#: dcim/forms/formsets.py:20 +#, python-brace-format +msgid "A virtual chassis member already exists in position {vc_position}." +msgstr "" + +#: dcim/forms/model_forms.py:101 dcim/tables/devices.py:183 +#: templates/dcim/sitegroup.html:26 +msgid "Site Group" +msgstr "" + +#: dcim/forms/model_forms.py:142 +msgid "Contact Info" +msgstr "" + +#: dcim/forms/model_forms.py:197 templates/dcim/rackrole.html:20 +msgid "Rack Role" +msgstr "" + +#: dcim/forms/model_forms.py:248 +msgid "" +"Comma-separated list of numeric unit IDs. A range may be specified using a " +"hyphen." +msgstr "" + +#: dcim/forms/model_forms.py:259 dcim/tables/racks.py:133 +msgid "Reservation" +msgstr "" + +#: dcim/forms/model_forms.py:297 dcim/forms/model_forms.py:380 +#: utilities/forms/fields/fields.py:47 +msgid "Slug" +msgstr "" + +#: dcim/forms/model_forms.py:304 templates/dcim/devicetype.html:12 +msgid "Chassis" +msgstr "" + +#: dcim/forms/model_forms.py:356 templates/dcim/devicerole.html:24 +msgid "Device Role" +msgstr "" + +#: dcim/forms/model_forms.py:424 dcim/models/devices.py:632 +msgid "The lowest-numbered unit occupied by the device" +msgstr "" + +#: dcim/forms/model_forms.py:468 +msgid "The position in the virtual chassis this device is identified by" +msgstr "" + +#: dcim/forms/model_forms.py:472 templates/dcim/device.html:148 +#: templates/dcim/virtualchassis.html:61 +#: templates/dcim/virtualchassis_edit.html:57 +#: templates/ipam/inc/panels/fhrp_groups.html:13 tenancy/forms/bulk_edit.py:146 +#: tenancy/forms/filtersets.py:111 +msgid "Priority" +msgstr "" + +#: dcim/forms/model_forms.py:473 +msgid "The priority of the device in the virtual chassis" +msgstr "" + +#: dcim/forms/model_forms.py:577 +msgid "Automatically populate components associated with this module type" +msgstr "" + +#: dcim/forms/model_forms.py:622 +msgid "Maximum length is 32767 (any unit)" +msgstr "" + +#: dcim/forms/model_forms.py:670 +msgid "Characteristics" +msgstr "" + +#: dcim/forms/model_forms.py:1129 +msgid "LAG interface" +msgstr "" + +#: dcim/forms/model_forms.py:1183 dcim/forms/model_forms.py:1344 +#: dcim/tables/connections.py:65 ipam/forms/bulk_import.py:320 +#: ipam/forms/bulk_import.py:557 ipam/forms/model_forms.py:272 +#: ipam/forms/model_forms.py:281 ipam/forms/model_forms.py:807 +#: ipam/forms/model_forms.py:816 ipam/tables/fhrp.py:64 ipam/tables/ip.py:368 +#: ipam/tables/vlans.py:165 templates/circuits/inc/circuit_termination.html:78 +#: templates/dcim/frontport.html:113 templates/dcim/interface.html:27 +#: templates/dcim/interface.html:186 templates/dcim/interface.html:318 +#: templates/dcim/inventoryitem_edit.html:54 templates/dcim/rearport.html:109 +#: templates/ipam/fhrpgroupassignment_edit.html:11 +#: templates/virtualization/vminterface.html:19 +#: templates/wireless/inc/wirelesslink_interface.html:10 +#: templates/wireless/wirelesslink.html:10 +#: templates/wireless/wirelesslink.html:49 +#: virtualization/forms/model_forms.py:330 wireless/forms/model_forms.py:112 +#: wireless/forms/model_forms.py:152 +msgid "Interface" +msgstr "" + +#: dcim/forms/model_forms.py:1277 +msgid "Child Device" +msgstr "" + +#: dcim/forms/model_forms.py:1278 +msgid "" +"Child devices must first be created and assigned to the site and rack of the " +"parent device." +msgstr "" + +#: dcim/forms/model_forms.py:1320 +msgid "Console port" +msgstr "" + +#: dcim/forms/model_forms.py:1328 +msgid "Console server port" +msgstr "" + +#: dcim/forms/model_forms.py:1336 +msgid "Front port" +msgstr "" + +#: dcim/forms/model_forms.py:1352 +msgid "Power outlet" +msgstr "" + +#: dcim/forms/model_forms.py:1372 templates/dcim/inventoryitem.html:17 +#: templates/dcim/inventoryitem_edit.html:10 +msgid "Inventory Item" +msgstr "" + +#: dcim/forms/model_forms.py:1424 +msgid "An InventoryItem can only be assigned to a single component." +msgstr "" + +#: dcim/forms/model_forms.py:1438 templates/dcim/inventoryitemrole.html:15 +msgid "Inventory Item Role" +msgstr "" + +#: dcim/forms/model_forms.py:1458 templates/dcim/device.html:212 +#: templates/dcim/virtualdevicecontext.html:33 +#: templates/virtualization/virtualmachine.html:51 +msgid "Primary IPv4" +msgstr "" + +#: dcim/forms/model_forms.py:1467 templates/dcim/device.html:228 +#: templates/dcim/virtualdevicecontext.html:44 +#: templates/virtualization/virtualmachine.html:67 +msgid "Primary IPv6" +msgstr "" + +#: dcim/forms/object_create.py:47 dcim/forms/object_create.py:181 +#: dcim/forms/object_create.py:321 +msgid "" +"Alphanumeric ranges are supported. (Must match the number of objects being " +"created.)" +msgstr "" + +#: dcim/forms/object_create.py:67 +#, python-brace-format +msgid "" +"The provided pattern specifies {value_count} values, but {pattern_count} are " +"expected." +msgstr "" + +#: dcim/forms/object_create.py:109 dcim/forms/object_create.py:253 +#: dcim/tables/devices.py:281 +msgid "Rear ports" +msgstr "" + +#: dcim/forms/object_create.py:110 dcim/forms/object_create.py:254 +msgid "Select one rear port assignment for each front port being created." +msgstr "" + +#: dcim/forms/object_create.py:233 +#, python-brace-format +msgid "" +"The string {module} will be replaced with the position of the " +"assigned module, if any." +msgstr "" + +#: dcim/forms/object_create.py:375 dcim/tables/devices.py:1013 +#: ipam/tables/fhrp.py:31 templates/dcim/virtualchassis.html:54 +#: templates/dcim/virtualchassis_edit.html:48 templates/ipam/fhrpgroup.html:39 +msgid "Members" +msgstr "" + +#: dcim/forms/object_create.py:384 +msgid "Initial position" +msgstr "" + +#: dcim/forms/object_create.py:387 +msgid "" +"Position of the first member device. Increases by one for each additional " +"member." +msgstr "" + +#: dcim/forms/object_create.py:401 +msgid "A position must be specified for the first VC member." +msgstr "" + +#: dcim/models/cables.py:63 dcim/models/device_component_templates.py:56 +#: dcim/models/device_components.py:64 extras/models/customfields.py:102 +msgid "label" +msgstr "" + +#: dcim/models/cables.py:72 +msgid "length" +msgstr "" + +#: dcim/models/cables.py:79 +msgid "length unit" +msgstr "" + +#: dcim/models/cables.py:94 +msgid "cable" +msgstr "" + +#: dcim/models/cables.py:95 +msgid "cables" +msgstr "" + +#: dcim/models/cables.py:247 ipam/models/asns.py:37 +msgid "end" +msgstr "" + +#: dcim/models/cables.py:297 +msgid "cable termination" +msgstr "" + +#: dcim/models/cables.py:298 +msgid "cable terminations" +msgstr "" + +#: dcim/models/cables.py:421 extras/models/configs.py:50 +msgid "is active" +msgstr "" + +#: dcim/models/cables.py:425 +msgid "is complete" +msgstr "" + +#: dcim/models/cables.py:429 +msgid "is split" +msgstr "" + +#: dcim/models/cables.py:435 +msgid "cable path" +msgstr "" + +#: dcim/models/cables.py:436 +msgid "cable paths" +msgstr "" + +#: dcim/models/device_component_templates.py:47 +#, python-brace-format +msgid "" +"{module} is accepted as a substitution for the module bay position when " +"attached to a module type." +msgstr "" + +#: dcim/models/device_component_templates.py:59 +#: dcim/models/device_components.py:67 +msgid "Physical label" +msgstr "" + +#: dcim/models/device_component_templates.py:104 +msgid "Component templates cannot be moved to a different device type." +msgstr "" + +#: dcim/models/device_component_templates.py:155 +msgid "" +"A component template cannot be associated with both a device type and a " +"module type." +msgstr "" + +#: dcim/models/device_component_templates.py:159 +msgid "" +"A component template must be associated with either a device type or a " +"module type." +msgstr "" + +#: dcim/models/device_component_templates.py:187 +msgid "console port template" +msgstr "" + +#: dcim/models/device_component_templates.py:188 +msgid "console port templates" +msgstr "" + +#: dcim/models/device_component_templates.py:221 +msgid "console server port template" +msgstr "" + +#: dcim/models/device_component_templates.py:222 +msgid "console server port templates" +msgstr "" + +#: dcim/models/device_component_templates.py:253 +#: dcim/models/device_components.py:354 +msgid "maximum draw" +msgstr "" + +#: dcim/models/device_component_templates.py:260 +#: dcim/models/device_components.py:361 +msgid "allocated draw" +msgstr "" + +#: dcim/models/device_component_templates.py:270 +msgid "power port template" +msgstr "" + +#: dcim/models/device_component_templates.py:271 +msgid "power port templates" +msgstr "" + +#: dcim/models/device_component_templates.py:290 +#: dcim/models/device_components.py:384 +#, python-brace-format +msgid "Allocated draw cannot exceed the maximum draw ({maximum_draw}W)." +msgstr "" + +#: dcim/models/device_component_templates.py:322 +#: dcim/models/device_components.py:479 +msgid "feed leg" +msgstr "" + +#: dcim/models/device_component_templates.py:326 +#: dcim/models/device_components.py:483 +msgid "Phase (for three-phase feeds)" +msgstr "" + +#: dcim/models/device_component_templates.py:332 +msgid "power outlet template" +msgstr "" + +#: dcim/models/device_component_templates.py:333 +msgid "power outlet templates" +msgstr "" + +#: dcim/models/device_component_templates.py:342 +#, python-brace-format +msgid "Parent power port ({power_port}) must belong to the same device type" +msgstr "" + +#: dcim/models/device_component_templates.py:346 +#, python-brace-format +msgid "Parent power port ({power_port}) must belong to the same module type" +msgstr "" + +#: dcim/models/device_component_templates.py:398 +#: dcim/models/device_components.py:609 +msgid "management only" +msgstr "" + +#: dcim/models/device_component_templates.py:406 +#: dcim/models/device_components.py:552 +msgid "bridge interface" +msgstr "" + +#: dcim/models/device_component_templates.py:424 +#: dcim/models/device_components.py:634 +msgid "wireless role" +msgstr "" + +#: dcim/models/device_component_templates.py:430 +msgid "interface template" +msgstr "" + +#: dcim/models/device_component_templates.py:431 +msgid "interface templates" +msgstr "" + +#: dcim/models/device_component_templates.py:438 +#: dcim/models/device_components.py:796 +#: virtualization/models/virtualmachines.py:340 +msgid "An interface cannot be bridged to itself." +msgstr "" + +#: dcim/models/device_component_templates.py:441 +#, python-brace-format +msgid "Bridge interface ({bridge}) must belong to the same device type" +msgstr "" + +#: dcim/models/device_component_templates.py:445 +#, python-brace-format +msgid "Bridge interface ({bridge}) must belong to the same module type" +msgstr "" + +#: dcim/models/device_component_templates.py:501 +#: dcim/models/device_components.py:976 +msgid "rear port position" +msgstr "" + +#: dcim/models/device_component_templates.py:526 +msgid "front port template" +msgstr "" + +#: dcim/models/device_component_templates.py:527 +msgid "front port templates" +msgstr "" + +#: dcim/models/device_component_templates.py:537 +#, python-brace-format +msgid "Rear port ({name}) must belong to the same device type" +msgstr "" + +#: dcim/models/device_component_templates.py:543 +#, python-brace-format +msgid "" +"Invalid rear port position ({position}); rear port {name} has only {count} " +"positions" +msgstr "" + +#: dcim/models/device_component_templates.py:596 +#: dcim/models/device_components.py:1045 +msgid "positions" +msgstr "" + +#: dcim/models/device_component_templates.py:607 +msgid "rear port template" +msgstr "" + +#: dcim/models/device_component_templates.py:608 +msgid "rear port templates" +msgstr "" + +#: dcim/models/device_component_templates.py:637 +#: dcim/models/device_components.py:1086 +msgid "position" +msgstr "" + +#: dcim/models/device_component_templates.py:640 +#: dcim/models/device_components.py:1089 +msgid "Identifier to reference when renaming installed components" +msgstr "" + +#: dcim/models/device_component_templates.py:646 +msgid "module bay template" +msgstr "" + +#: dcim/models/device_component_templates.py:647 +msgid "module bay templates" +msgstr "" + +#: dcim/models/device_component_templates.py:674 +msgid "device bay template" +msgstr "" + +#: dcim/models/device_component_templates.py:675 +msgid "device bay templates" +msgstr "" + +#: dcim/models/device_component_templates.py:688 +#, python-brace-format +msgid "" +"Subdevice role of device type ({device_type}) must be set to \"parent\" to " +"allow device bays." +msgstr "" + +#: dcim/models/device_component_templates.py:743 +#: dcim/models/device_components.py:1215 +msgid "part ID" +msgstr "" + +#: dcim/models/device_component_templates.py:745 +#: dcim/models/device_components.py:1217 +msgid "Manufacturer-assigned part identifier" +msgstr "" + +#: dcim/models/device_component_templates.py:759 +msgid "inventory item template" +msgstr "" + +#: dcim/models/device_component_templates.py:760 +msgid "inventory item templates" +msgstr "" + +#: dcim/models/device_components.py:107 +msgid "Components cannot be moved to a different device." +msgstr "" + +#: dcim/models/device_components.py:146 +msgid "cable end" +msgstr "" + +#: dcim/models/device_components.py:152 +msgid "mark connected" +msgstr "" + +#: dcim/models/device_components.py:154 +msgid "Treat as if a cable is connected" +msgstr "" + +#: dcim/models/device_components.py:172 +msgid "Must specify cable end (A or B) when attaching a cable." +msgstr "" + +#: dcim/models/device_components.py:176 +msgid "Cable end must not be set without a cable." +msgstr "" + +#: dcim/models/device_components.py:180 +msgid "Cannot mark as connected with a cable attached." +msgstr "" + +#: dcim/models/device_components.py:204 +#, python-brace-format +msgid "{class_name} models must declare a parent_object property" +msgstr "" + +#: dcim/models/device_components.py:289 dcim/models/device_components.py:318 +#: dcim/models/device_components.py:351 dcim/models/device_components.py:469 +msgid "Physical port type" +msgstr "" + +#: dcim/models/device_components.py:292 dcim/models/device_components.py:321 +msgid "speed" +msgstr "" + +#: dcim/models/device_components.py:296 dcim/models/device_components.py:325 +msgid "Port speed in bits per second" +msgstr "" + +#: dcim/models/device_components.py:302 +msgid "console port" +msgstr "" + +#: dcim/models/device_components.py:303 +msgid "console ports" +msgstr "" + +#: dcim/models/device_components.py:331 +msgid "console server port" +msgstr "" + +#: dcim/models/device_components.py:332 +msgid "console server ports" +msgstr "" + +#: dcim/models/device_components.py:371 +msgid "power port" +msgstr "" + +#: dcim/models/device_components.py:372 +msgid "power ports" +msgstr "" + +#: dcim/models/device_components.py:489 +msgid "power outlet" +msgstr "" + +#: dcim/models/device_components.py:490 +msgid "power outlets" +msgstr "" + +#: dcim/models/device_components.py:501 +#, python-brace-format +msgid "Parent power port ({power_port}) must belong to the same device" +msgstr "" + +#: dcim/models/device_components.py:532 +msgid "mode" +msgstr "" + +#: dcim/models/device_components.py:536 +msgid "IEEE 802.1Q tagging strategy" +msgstr "" + +#: dcim/models/device_components.py:544 +msgid "parent interface" +msgstr "" + +#: dcim/models/device_components.py:600 +msgid "parent LAG" +msgstr "" + +#: dcim/models/device_components.py:610 +msgid "This interface is used only for out-of-band management" +msgstr "" + +#: dcim/models/device_components.py:615 +msgid "speed (Kbps)" +msgstr "" + +#: dcim/models/device_components.py:618 +msgid "duplex" +msgstr "" + +#: dcim/models/device_components.py:628 +msgid "64-bit World Wide Name" +msgstr "" + +#: dcim/models/device_components.py:640 +msgid "wireless channel" +msgstr "" + +#: dcim/models/device_components.py:647 +msgid "channel frequency (MHz)" +msgstr "" + +#: dcim/models/device_components.py:648 dcim/models/device_components.py:656 +msgid "Populated by selected channel (if set)" +msgstr "" + +#: dcim/models/device_components.py:662 +msgid "transmit power (dBm)" +msgstr "" + +#: dcim/models/device_components.py:687 wireless/models.py:116 +msgid "wireless LANs" +msgstr "" + +#: dcim/models/device_components.py:695 +#: virtualization/models/virtualmachines.py:266 +msgid "untagged VLAN" +msgstr "" + +#: dcim/models/device_components.py:701 +#: virtualization/models/virtualmachines.py:272 +msgid "tagged VLANs" +msgstr "" + +#: dcim/models/device_components.py:737 +#: virtualization/models/virtualmachines.py:309 +msgid "interface" +msgstr "" + +#: dcim/models/device_components.py:738 +#: virtualization/models/virtualmachines.py:310 +msgid "interfaces" +msgstr "" + +#: dcim/models/device_components.py:749 +#, python-brace-format +msgid "{display_type} interfaces cannot have a cable attached." +msgstr "" + +#: dcim/models/device_components.py:757 +#, python-brace-format +msgid "{display_type} interfaces cannot be marked as connected." +msgstr "" + +#: dcim/models/device_components.py:766 +#: virtualization/models/virtualmachines.py:325 +msgid "An interface cannot be its own parent." +msgstr "" + +#: dcim/models/device_components.py:770 +msgid "Only virtual interfaces may be assigned to a parent interface." +msgstr "" + +#: dcim/models/device_components.py:777 +#, python-brace-format +msgid "" +"The selected parent interface ({interface}) belongs to a different device " +"({device})" +msgstr "" + +#: dcim/models/device_components.py:783 +#, python-brace-format +msgid "" +"The selected parent interface ({interface}) belongs to {device}, which is " +"not part of virtual chassis {virtual_chassis}." +msgstr "" + +#: dcim/models/device_components.py:803 +#, python-brace-format +msgid "" +"The selected bridge interface ({bridge}) belongs to a different device " +"({device})." +msgstr "" + +#: dcim/models/device_components.py:809 +#, python-brace-format +msgid "" +"The selected bridge interface ({interface}) belongs to {device}, which is " +"not part of virtual chassis {virtual_chassis}." +msgstr "" + +#: dcim/models/device_components.py:820 +msgid "Virtual interfaces cannot have a parent LAG interface." +msgstr "" + +#: dcim/models/device_components.py:824 +msgid "A LAG interface cannot be its own parent." +msgstr "" + +#: dcim/models/device_components.py:831 +#, python-brace-format +msgid "" +"The selected LAG interface ({lag}) belongs to a different device ({device})." +msgstr "" + +#: dcim/models/device_components.py:837 +#, python-brace-format +msgid "" +"The selected LAG interface ({lag}) belongs to {device}, which is not part of " +"virtual chassis {virtual_chassis}." +msgstr "" + +#: dcim/models/device_components.py:848 +msgid "Virtual interfaces cannot have a PoE mode." +msgstr "" + +#: dcim/models/device_components.py:852 +msgid "Virtual interfaces cannot have a PoE type." +msgstr "" + +#: dcim/models/device_components.py:858 +msgid "Must specify PoE mode when designating a PoE type." +msgstr "" + +#: dcim/models/device_components.py:865 +msgid "Wireless role may be set only on wireless interfaces." +msgstr "" + +#: dcim/models/device_components.py:867 +msgid "Channel may be set only on wireless interfaces." +msgstr "" + +#: dcim/models/device_components.py:873 +msgid "Channel frequency may be set only on wireless interfaces." +msgstr "" + +#: dcim/models/device_components.py:877 +msgid "Cannot specify custom frequency with channel selected." +msgstr "" + +#: dcim/models/device_components.py:883 +msgid "Channel width may be set only on wireless interfaces." +msgstr "" + +#: dcim/models/device_components.py:885 +msgid "Cannot specify custom width with channel selected." +msgstr "" + +#: dcim/models/device_components.py:893 +#, 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 "" + +#: dcim/models/device_components.py:982 +msgid "Mapped position on corresponding rear port" +msgstr "" + +#: dcim/models/device_components.py:998 +msgid "front port" +msgstr "" + +#: dcim/models/device_components.py:999 +msgid "front ports" +msgstr "" + +#: dcim/models/device_components.py:1013 +#, python-brace-format +msgid "Rear port ({rear_port}) must belong to the same device" +msgstr "" + +#: dcim/models/device_components.py:1021 +#, python-brace-format +msgid "" +"Invalid rear port position ({rear_port_position}): Rear port {name} has only " +"{positions} positions." +msgstr "" + +#: dcim/models/device_components.py:1051 +msgid "Number of front ports which may be mapped" +msgstr "" + +#: dcim/models/device_components.py:1056 +msgid "rear port" +msgstr "" + +#: dcim/models/device_components.py:1057 +msgid "rear ports" +msgstr "" + +#: dcim/models/device_components.py:1071 +#, python-brace-format +msgid "" +"The number of positions cannot be less than the number of mapped front ports " +"({frontport_count})" +msgstr "" + +#: dcim/models/device_components.py:1095 +msgid "module bay" +msgstr "" + +#: dcim/models/device_components.py:1096 +msgid "module bays" +msgstr "" + +#: dcim/models/device_components.py:1109 +msgid "parent_bay" +msgstr "" + +#: dcim/models/device_components.py:1117 +msgid "device bay" +msgstr "" + +#: dcim/models/device_components.py:1118 +msgid "device bays" +msgstr "" + +#: dcim/models/device_components.py:1128 +#, python-brace-format +msgid "This type of device ({device_type}) does not support device bays." +msgstr "" + +#: dcim/models/device_components.py:1134 +msgid "Cannot install a device into itself." +msgstr "" + +#: dcim/models/device_components.py:1142 +#, python-brace-format +msgid "" +"Cannot install the specified device; device is already installed in {bay}." +msgstr "" + +#: dcim/models/device_components.py:1163 +msgid "inventory item role" +msgstr "" + +#: dcim/models/device_components.py:1164 +msgid "inventory item roles" +msgstr "" + +#: dcim/models/device_components.py:1221 dcim/models/devices.py:595 +#: dcim/models/devices.py:1168 dcim/models/racks.py:113 +msgid "serial number" +msgstr "" + +#: dcim/models/device_components.py:1229 dcim/models/devices.py:603 +#: dcim/models/devices.py:1175 dcim/models/racks.py:120 +msgid "asset tag" +msgstr "" + +#: dcim/models/device_components.py:1230 +msgid "A unique tag used to identify this item" +msgstr "" + +#: dcim/models/device_components.py:1233 +msgid "discovered" +msgstr "" + +#: dcim/models/device_components.py:1235 +msgid "This item was automatically discovered" +msgstr "" + +#: dcim/models/device_components.py:1250 +msgid "inventory item" +msgstr "" + +#: dcim/models/device_components.py:1251 +msgid "inventory items" +msgstr "" + +#: dcim/models/device_components.py:1262 +msgid "Cannot assign self as parent." +msgstr "" + +#: dcim/models/device_components.py:1270 +msgid "Parent inventory item does not belong to the same device." +msgstr "" + +#: dcim/models/device_components.py:1276 +msgid "Cannot move an inventory item with dependent children" +msgstr "" + +#: dcim/models/device_components.py:1284 +msgid "Cannot assign inventory item to component on another device" +msgstr "" + +#: dcim/models/devices.py:54 +msgid "manufacturer" +msgstr "" + +#: dcim/models/devices.py:55 +msgid "manufacturers" +msgstr "" + +#: dcim/models/devices.py:82 dcim/models/devices.py:381 +msgid "model" +msgstr "" + +#: dcim/models/devices.py:95 +msgid "default platform" +msgstr "" + +#: dcim/models/devices.py:98 dcim/models/devices.py:385 +msgid "part number" +msgstr "" + +#: dcim/models/devices.py:101 dcim/models/devices.py:388 +msgid "Discrete part number (optional)" +msgstr "" + +#: dcim/models/devices.py:107 dcim/models/racks.py:137 +msgid "height (U)" +msgstr "" + +#: dcim/models/devices.py:111 +msgid "exclude from utilization" +msgstr "" + +#: dcim/models/devices.py:112 +msgid "Exclude from rack utilization calculations." +msgstr "" + +#: dcim/models/devices.py:116 +msgid "is full depth" +msgstr "" + +#: dcim/models/devices.py:117 +msgid "Device consumes both front and rear rack faces" +msgstr "" + +#: dcim/models/devices.py:123 +msgid "parent/child status" +msgstr "" + +#: dcim/models/devices.py:124 +msgid "" +"Parent devices house child devices in device bays. Leave blank if this " +"device type is neither a parent nor a child." +msgstr "" + +#: dcim/models/devices.py:128 dcim/models/devices.py:647 +msgid "airflow" +msgstr "" + +#: dcim/models/devices.py:204 +msgid "device type" +msgstr "" + +#: dcim/models/devices.py:205 +msgid "device types" +msgstr "" + +#: dcim/models/devices.py:289 +msgid "U height must be in increments of 0.5 rack units." +msgstr "" + +#: dcim/models/devices.py:306 +#, python-brace-format +msgid "" +"Device {device} in rack {rack} does not have sufficient space to accommodate " +"a height of {height}U" +msgstr "" + +#: dcim/models/devices.py:321 +#, python-brace-format +msgid "" +"Unable to set 0U height: Found {racked_instance_count} " +"instances already mounted within racks." +msgstr "" + +#: dcim/models/devices.py:330 +msgid "" +"Must delete all device bay templates associated with this device before " +"declassifying it as a parent device." +msgstr "" + +#: dcim/models/devices.py:336 +msgid "Child device types must be 0U." +msgstr "" + +#: dcim/models/devices.py:404 +msgid "module type" +msgstr "" + +#: dcim/models/devices.py:405 +msgid "module types" +msgstr "" + +#: dcim/models/devices.py:473 +msgid "Virtual machines may be assigned to this role" +msgstr "" + +#: dcim/models/devices.py:485 +msgid "device role" +msgstr "" + +#: dcim/models/devices.py:486 +msgid "device roles" +msgstr "" + +#: dcim/models/devices.py:503 +msgid "Optionally limit this platform to devices of a certain manufacturer" +msgstr "" + +#: dcim/models/devices.py:515 +msgid "platform" +msgstr "" + +#: dcim/models/devices.py:516 +msgid "platforms" +msgstr "" + +#: dcim/models/devices.py:564 +msgid "The function this device serves" +msgstr "" + +#: dcim/models/devices.py:596 +msgid "Chassis serial number, assigned by the manufacturer" +msgstr "" + +#: dcim/models/devices.py:604 dcim/models/devices.py:1176 +msgid "A unique tag used to identify this device" +msgstr "" + +#: dcim/models/devices.py:631 +msgid "position (U)" +msgstr "" + +#: dcim/models/devices.py:638 +msgid "rack face" +msgstr "" + +#: dcim/models/devices.py:658 dcim/models/devices.py:1385 +#: virtualization/models/virtualmachines.py:97 +msgid "primary IPv4" +msgstr "" + +#: dcim/models/devices.py:666 dcim/models/devices.py:1393 +#: virtualization/models/virtualmachines.py:105 +msgid "primary IPv6" +msgstr "" + +#: dcim/models/devices.py:674 +msgid "out-of-band IP" +msgstr "" + +#: dcim/models/devices.py:691 +msgid "VC position" +msgstr "" + +#: dcim/models/devices.py:695 +msgid "Virtual chassis position" +msgstr "" + +#: dcim/models/devices.py:698 +msgid "VC priority" +msgstr "" + +#: dcim/models/devices.py:702 +msgid "Virtual chassis master election priority" +msgstr "" + +#: dcim/models/devices.py:705 dcim/models/sites.py:207 +msgid "latitude" +msgstr "" + +#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/sites.py:212 dcim/models/sites.py:220 +msgid "GPS coordinate in decimal format (xx.yyyyyy)" +msgstr "" + +#: dcim/models/devices.py:713 dcim/models/sites.py:215 +msgid "longitude" +msgstr "" + +#: dcim/models/devices.py:786 +msgid "Device name must be unique per site." +msgstr "" + +#: dcim/models/devices.py:797 ipam/models/services.py:75 +msgid "device" +msgstr "" + +#: dcim/models/devices.py:798 +msgid "devices" +msgstr "" + +#: dcim/models/devices.py:838 +#, python-brace-format +msgid "Rack {rack} does not belong to site {site}." +msgstr "" + +#: dcim/models/devices.py:843 +#, python-brace-format +msgid "Location {location} does not belong to site {site}." +msgstr "" + +#: dcim/models/devices.py:849 +#, python-brace-format +msgid "Rack {rack} does not belong to location {location}." +msgstr "" + +#: dcim/models/devices.py:856 +msgid "Cannot select a rack face without assigning a rack." +msgstr "" + +#: dcim/models/devices.py:860 +msgid "Cannot select a rack position without assigning a rack." +msgstr "" + +#: dcim/models/devices.py:866 +msgid "Position must be in increments of 0.5 rack units." +msgstr "" + +#: dcim/models/devices.py:870 +msgid "Must specify rack face when defining rack position." +msgstr "" + +#: dcim/models/devices.py:878 +#, python-brace-format +msgid "A U0 device type ({device_type}) cannot be assigned to a rack position." +msgstr "" + +#: dcim/models/devices.py:889 +msgid "" +"Child device types cannot be assigned to a rack face. This is an attribute " +"of the parent device." +msgstr "" + +#: dcim/models/devices.py:896 +msgid "" +"Child device types cannot be assigned to a rack position. This is an " +"attribute of the parent device." +msgstr "" + +#: dcim/models/devices.py:910 +#, 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 "" + +#: dcim/models/devices.py:925 +#, python-brace-format +msgid "{ip} is not an IPv4 address." +msgstr "" + +#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#, python-brace-format +msgid "The specified IP address ({ip}) is not assigned to this device." +msgstr "" + +#: dcim/models/devices.py:940 +#, python-brace-format +msgid "{ip} is not an IPv6 address." +msgstr "" + +#: dcim/models/devices.py:967 +#, python-brace-format +msgid "" +"The assigned platform is limited to {platform_manufacturer} device types, " +"but this device's type belongs to {devicetype_manufacturer}." +msgstr "" + +#: dcim/models/devices.py:978 +#, python-brace-format +msgid "The assigned cluster belongs to a different site ({site})" +msgstr "" + +#: dcim/models/devices.py:986 +msgid "A device assigned to a virtual chassis must have its position defined." +msgstr "" + +#: dcim/models/devices.py:1183 +msgid "module" +msgstr "" + +#: dcim/models/devices.py:1184 +msgid "modules" +msgstr "" + +#: dcim/models/devices.py:1200 +#, python-brace-format +msgid "" +"Module must be installed within a module bay belonging to the assigned " +"device ({device})." +msgstr "" + +#: dcim/models/devices.py:1304 +msgid "domain" +msgstr "" + +#: dcim/models/devices.py:1317 dcim/models/devices.py:1318 +msgid "virtual chassis" +msgstr "" + +#: dcim/models/devices.py:1333 +#, python-brace-format +msgid "The selected master ({master}) is not assigned to this virtual chassis." +msgstr "" + +#: dcim/models/devices.py:1349 +#, python-brace-format +msgid "" +"Unable to delete virtual chassis {self}. There are member interfaces which " +"form a cross-chassis LAG interfaces." +msgstr "" + +#: dcim/models/devices.py:1374 ipam/models/l2vpn.py:37 +msgid "identifier" +msgstr "" + +#: dcim/models/devices.py:1375 +msgid "Numeric identifier unique to the parent device" +msgstr "" + +#: dcim/models/devices.py:1403 extras/models/models.py:629 +#: netbox/models/__init__.py:114 +msgid "comments" +msgstr "" + +#: dcim/models/devices.py:1419 +msgid "virtual device context" +msgstr "" + +#: dcim/models/devices.py:1420 +msgid "virtual device contexts" +msgstr "" + +#: dcim/models/devices.py:1452 +#, python-brace-format +msgid "{ip} is not an IPv{family} address." +msgstr "" + +#: dcim/models/devices.py:1458 +msgid "Primary IP address must belong to an interface on the assigned device." +msgstr "" + +#: dcim/models/mixins.py:15 extras/models/configs.py:41 +#: extras/models/models.py:260 extras/models/models.py:469 +#: extras/models/search.py:48 ipam/models/ip.py:193 +msgid "weight" +msgstr "" + +#: dcim/models/mixins.py:22 +msgid "weight unit" +msgstr "" + +#: dcim/models/mixins.py:51 +msgid "Must specify a unit when setting a weight" +msgstr "" + +#: dcim/models/power.py:55 +msgid "power panel" +msgstr "" + +#: dcim/models/power.py:56 +msgid "power panels" +msgstr "" + +#: dcim/models/power.py:70 +#, python-brace-format +msgid "" +"Location {location} ({location_site}) is in a different site than {site}" +msgstr "" + +#: dcim/models/power.py:107 +msgid "supply" +msgstr "" + +#: dcim/models/power.py:113 +msgid "phase" +msgstr "" + +#: dcim/models/power.py:119 +msgid "voltage" +msgstr "" + +#: dcim/models/power.py:124 +msgid "amperage" +msgstr "" + +#: dcim/models/power.py:129 +msgid "max utilization" +msgstr "" + +#: dcim/models/power.py:132 +msgid "Maximum permissible draw (percentage)" +msgstr "" + +#: dcim/models/power.py:135 +msgid "available power" +msgstr "" + +#: dcim/models/power.py:163 +msgid "power feed" +msgstr "" + +#: dcim/models/power.py:164 +msgid "power feeds" +msgstr "" + +#: dcim/models/power.py:178 +#, python-brace-format +msgid "" +"Rack {rack} ({site}) and power panel {powerpanel} ({powerpanel_site}) are in " +"different sites" +msgstr "" + +#: dcim/models/power.py:189 +msgid "Voltage cannot be negative for AC supply" +msgstr "" + +#: dcim/models/racks.py:49 +msgid "rack role" +msgstr "" + +#: dcim/models/racks.py:50 +msgid "rack roles" +msgstr "" + +#: dcim/models/racks.py:74 +msgid "facility ID" +msgstr "" + +#: dcim/models/racks.py:75 +msgid "Locally-assigned identifier" +msgstr "" + +#: dcim/models/racks.py:108 ipam/forms/bulk_import.py:203 +#: ipam/forms/bulk_import.py:268 ipam/forms/bulk_import.py:303 +#: ipam/forms/bulk_import.py:470 virtualization/forms/bulk_import.py:111 +msgid "Functional role" +msgstr "" + +#: dcim/models/racks.py:121 +msgid "A unique tag used to identify this rack" +msgstr "" + +#: dcim/models/racks.py:132 +msgid "width" +msgstr "" + +#: dcim/models/racks.py:133 +msgid "Rail-to-rail width" +msgstr "" + +#: dcim/models/racks.py:139 +msgid "Height in rack units" +msgstr "" + +#: dcim/models/racks.py:143 +msgid "starting unit" +msgstr "" + +#: dcim/models/racks.py:144 +msgid "Starting unit for rack" +msgstr "" + +#: dcim/models/racks.py:148 +msgid "descending units" +msgstr "" + +#: dcim/models/racks.py:149 +msgid "Units are numbered top-to-bottom" +msgstr "" + +#: dcim/models/racks.py:152 +msgid "outer width" +msgstr "" + +#: dcim/models/racks.py:155 +msgid "Outer dimension of rack (width)" +msgstr "" + +#: dcim/models/racks.py:158 +msgid "outer depth" +msgstr "" + +#: dcim/models/racks.py:161 +msgid "Outer dimension of rack (depth)" +msgstr "" + +#: dcim/models/racks.py:164 +msgid "outer unit" +msgstr "" + +#: dcim/models/racks.py:170 +msgid "max weight" +msgstr "" + +#: dcim/models/racks.py:173 +msgid "Maximum load capacity for the rack" +msgstr "" + +#: dcim/models/racks.py:181 +msgid "mounting depth" +msgstr "" + +#: dcim/models/racks.py:185 +msgid "" +"Maximum depth of a mounted device, in millimeters. For four-post racks, this " +"is the distance between the front and rear rails." +msgstr "" + +#: dcim/models/racks.py:219 +msgid "rack" +msgstr "" + +#: dcim/models/racks.py:220 +msgid "racks" +msgstr "" + +#: dcim/models/racks.py:235 +#, python-brace-format +msgid "Assigned location must belong to parent site ({site})." +msgstr "" + +#: dcim/models/racks.py:239 +msgid "Must specify a unit when setting an outer width/depth" +msgstr "" + +#: dcim/models/racks.py:243 +msgid "Must specify a unit when setting a maximum weight" +msgstr "" + +#: dcim/models/racks.py:253 +#, python-brace-format +msgid "" +"Rack must be at least {min_height}U tall to house currently installed " +"devices." +msgstr "" + +#: dcim/models/racks.py:260 +#, python-brace-format +msgid "" +"Rack unit numbering must begin at {position} or less to house currently " +"installed devices." +msgstr "" + +#: dcim/models/racks.py:268 +#, python-brace-format +msgid "Location must be from the same site, {site}." +msgstr "" + +#: dcim/models/racks.py:521 +msgid "units" +msgstr "" + +#: dcim/models/racks.py:547 +msgid "rack reservation" +msgstr "" + +#: dcim/models/racks.py:548 +msgid "rack reservations" +msgstr "" + +#: dcim/models/racks.py:565 +#, python-brace-format +msgid "Invalid unit(s) for {height}U rack: {unit_list}" +msgstr "" + +#: dcim/models/racks.py:578 +#, python-brace-format +msgid "The following units have already been reserved: {unit_list}" +msgstr "" + +#: dcim/models/sites.py:49 +msgid "A top-level region with this name already exists." +msgstr "" + +#: dcim/models/sites.py:59 +msgid "A top-level region with this slug already exists." +msgstr "" + +#: dcim/models/sites.py:62 +msgid "region" +msgstr "" + +#: dcim/models/sites.py:63 +msgid "regions" +msgstr "" + +#: dcim/models/sites.py:102 +msgid "A top-level site group with this name already exists." +msgstr "" + +#: dcim/models/sites.py:112 +msgid "A top-level site group with this slug already exists." +msgstr "" + +#: dcim/models/sites.py:115 +msgid "site group" +msgstr "" + +#: dcim/models/sites.py:116 +msgid "site groups" +msgstr "" + +#: dcim/models/sites.py:141 +msgid "Full name of the site" +msgstr "" + +#: dcim/models/sites.py:181 +msgid "facility" +msgstr "" + +#: dcim/models/sites.py:184 +msgid "Local facility ID or description" +msgstr "" + +#: dcim/models/sites.py:195 +msgid "physical address" +msgstr "" + +#: dcim/models/sites.py:198 +msgid "Physical location of the building" +msgstr "" + +#: dcim/models/sites.py:201 +msgid "shipping address" +msgstr "" + +#: dcim/models/sites.py:204 +msgid "If different from the physical address" +msgstr "" + +#: dcim/models/sites.py:238 +msgid "site" +msgstr "" + +#: dcim/models/sites.py:239 +msgid "sites" +msgstr "" + +#: dcim/models/sites.py:303 +msgid "A location with this name already exists within the specified site." +msgstr "" + +#: dcim/models/sites.py:313 +msgid "A location with this slug already exists within the specified site." +msgstr "" + +#: dcim/models/sites.py:316 +msgid "location" +msgstr "" + +#: dcim/models/sites.py:317 +msgid "locations" +msgstr "" + +#: dcim/models/sites.py:331 +#, python-brace-format +msgid "Parent location ({parent}) must belong to the same site ({site})." +msgstr "" + +#: dcim/tables/cables.py:54 +msgid "Termination A" +msgstr "" + +#: dcim/tables/cables.py:59 +msgid "Termination B" +msgstr "" + +#: dcim/tables/cables.py:65 wireless/tables/wirelesslink.py:22 +msgid "Device A" +msgstr "" + +#: dcim/tables/cables.py:71 wireless/tables/wirelesslink.py:31 +msgid "Device B" +msgstr "" + +#: dcim/tables/cables.py:77 +msgid "Location A" +msgstr "" + +#: dcim/tables/cables.py:83 +msgid "Location B" +msgstr "" + +#: dcim/tables/cables.py:89 +msgid "Rack A" +msgstr "" + +#: dcim/tables/cables.py:95 +msgid "Rack B" +msgstr "" + +#: dcim/tables/cables.py:101 +msgid "Site A" +msgstr "" + +#: dcim/tables/cables.py:107 +msgid "Site B" +msgstr "" + +#: dcim/tables/connections.py:27 templates/dcim/consoleport.html:18 +#: templates/dcim/consoleserverport.html:75 templates/dcim/frontport.html:119 +#: templates/dcim/inventoryitem_edit.html:39 +msgid "Console Port" +msgstr "" + +#: 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 "" + +#: dcim/tables/connections.py:46 dcim/tables/devices.py:518 +#: templates/dcim/inventoryitem_edit.html:64 templates/dcim/poweroutlet.html:47 +#: templates/dcim/powerport.html:18 +msgid "Power Port" +msgstr "" + +#: dcim/tables/devices.py:94 dcim/tables/devices.py:139 dcim/tables/racks.py:81 +#: dcim/tables/sites.py:143 netbox/navigation/menu.py:57 +#: netbox/navigation/menu.py:61 netbox/navigation/menu.py:63 +#: virtualization/forms/model_forms.py:124 virtualization/tables/clusters.py:83 +#: virtualization/views.py:211 +msgid "Devices" +msgstr "" + +#: dcim/tables/devices.py:99 dcim/tables/devices.py:144 +#: virtualization/tables/clusters.py:88 +msgid "VMs" +msgstr "" + +#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: extras/forms/model_forms.py:403 templates/dcim/device.html:131 +#: templates/dcim/device/render_config.html:11 +#: templates/dcim/device/render_config.html:15 +#: templates/dcim/devicerole.html:47 templates/dcim/platform.html:44 +#: templates/extras/configtemplate.html:10 +#: templates/virtualization/virtualmachine.html:47 +#: templates/virtualization/virtualmachine/render_config.html:11 +#: templates/virtualization/virtualmachine/render_config.html:15 +#: virtualization/tables/virtualmachines.py:88 +msgid "Config Template" +msgstr "" + +#: dcim/tables/devices.py:216 dcim/tables/devices.py:1048 +#: ipam/forms/model_forms.py:298 ipam/tables/ip.py:352 ipam/tables/ip.py:418 +#: ipam/tables/ip.py:441 templates/ipam/ipaddress.html:12 +#: templates/ipam/ipaddress_edit.html:14 +#: virtualization/tables/virtualmachines.py:79 +msgid "IP Address" +msgstr "" + +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1052 +#: virtualization/tables/virtualmachines.py:70 +msgid "IPv4 Address" +msgstr "" + +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1056 +#: virtualization/tables/virtualmachines.py:74 +msgid "IPv6 Address" +msgstr "" + +#: dcim/tables/devices.py:239 +msgid "VC Position" +msgstr "" + +#: dcim/tables/devices.py:242 +msgid "VC Priority" +msgstr "" + +#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: templates/dcim/devicebay_populate.html:16 +msgid "Parent Device" +msgstr "" + +#: dcim/tables/devices.py:254 +msgid "Position (Device Bay)" +msgstr "" + +#: dcim/tables/devices.py:263 +msgid "Console ports" +msgstr "" + +#: dcim/tables/devices.py:266 +msgid "Console server ports" +msgstr "" + +#: dcim/tables/devices.py:269 +msgid "Power ports" +msgstr "" + +#: dcim/tables/devices.py:272 +msgid "Power outlets" +msgstr "" + +#: dcim/tables/devices.py:275 dcim/tables/devices.py:1061 +#: dcim/tables/devicetypes.py:125 dcim/views.py:1002 dcim/views.py:1241 +#: dcim/views.py:1927 netbox/navigation/menu.py:82 +#: netbox/navigation/menu.py:220 templates/dcim/device/base.html:37 +#: templates/dcim/device_list.html:43 templates/dcim/devicetype/base.html:34 +#: templates/dcim/module.html:34 templates/dcim/moduletype/base.html:34 +#: templates/dcim/virtualdevicecontext.html:64 +#: templates/dcim/virtualdevicecontext.html:85 +#: templates/virtualization/virtualmachine_list.html:14 +#: virtualization/tables/virtualmachines.py:85 virtualization/views.py:368 +#: wireless/tables/wirelesslan.py:55 +msgid "Interfaces" +msgstr "" + +#: dcim/tables/devices.py:278 +msgid "Front ports" +msgstr "" + +#: dcim/tables/devices.py:284 +msgid "Device bays" +msgstr "" + +#: dcim/tables/devices.py:287 +msgid "Module bays" +msgstr "" + +#: dcim/tables/devices.py:290 +msgid "Inventory items" +msgstr "" + +#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: templates/dcim/modulebay.html:17 +msgid "Module Bay" +msgstr "" + +#: dcim/tables/devices.py:350 +msgid "Cable Color" +msgstr "" + +#: dcim/tables/devices.py:356 +msgid "Link Peers" +msgstr "" + +#: dcim/tables/devices.py:359 +msgid "Mark Connected" +msgstr "" + +#: dcim/tables/devices.py:567 ipam/forms/model_forms.py:709 +#: ipam/tables/fhrp.py:28 ipam/views.py:599 ipam/views.py:673 +#: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 +#: templates/dcim/interface.html:347 templates/ipam/ipaddress_bulk_add.html:15 +#: templates/ipam/service.html:43 templates/virtualization/vminterface.html:84 +msgid "IP Addresses" +msgstr "" + +#: dcim/tables/devices.py:573 netbox/navigation/menu.py:190 +#: templates/ipam/inc/panels/fhrp_groups.html:5 +msgid "FHRP Groups" +msgstr "" + +#: dcim/tables/devices.py:604 dcim/tables/devicetypes.py:224 +#: templates/dcim/interface.html:66 +msgid "Management Only" +msgstr "" + +#: dcim/tables/devices.py:612 +msgid "Wireless link" +msgstr "" + +#: dcim/tables/devices.py:622 +msgid "VDCs" +msgstr "" + +#: dcim/tables/devices.py:706 +#: templates/circuits/inc/circuit_termination.html:80 +#: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 +#: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 +#: templates/dcim/interface.html:192 templates/dcim/inventoryitem_edit.html:69 +#: templates/dcim/rearport.html:18 templates/dcim/rearport.html:115 +msgid "Rear Port" +msgstr "" + +#: dcim/tables/devices.py:871 templates/dcim/modulebay.html:51 +msgid "Installed Module" +msgstr "" + +#: dcim/tables/devices.py:874 +msgid "Module Serial" +msgstr "" + +#: dcim/tables/devices.py:878 +msgid "Module Asset Tag" +msgstr "" + +#: dcim/tables/devices.py:887 +msgid "Module Status" +msgstr "" + +#: dcim/tables/devices.py:929 dcim/tables/devicetypes.py:308 +#: templates/dcim/inventoryitem.html:41 +msgid "Component" +msgstr "" + +#: dcim/tables/devices.py:980 +msgid "Items" +msgstr "" + +#: dcim/tables/devicetypes.py:38 netbox/navigation/menu.py:72 +#: netbox/navigation/menu.py:74 +msgid "Device Types" +msgstr "" + +#: dcim/tables/devicetypes.py:43 netbox/navigation/menu.py:75 +msgid "Module Types" +msgstr "" + +#: dcim/tables/devicetypes.py:48 dcim/tables/devicetypes.py:140 +#: dcim/views.py:1077 dcim/views.py:2020 netbox/navigation/menu.py:91 +#: 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:5 +#: templates/dcim/inventoryitemrole.html:33 +msgid "Inventory Items" +msgstr "" + +#: dcim/tables/devicetypes.py:53 extras/forms/filtersets.py:354 +#: extras/forms/model_forms.py:311 netbox/navigation/menu.py:66 +msgid "Platforms" +msgstr "" + +#: dcim/tables/devicetypes.py:85 templates/dcim/devicetype.html:32 +msgid "Default Platform" +msgstr "" + +#: dcim/tables/devicetypes.py:89 templates/dcim/devicetype.html:48 +msgid "Full Depth" +msgstr "" + +#: dcim/tables/devicetypes.py:98 +msgid "U Height" +msgstr "" + +#: dcim/tables/devicetypes.py:110 dcim/tables/modules.py:26 +msgid "Instances" +msgstr "" + +#: dcim/tables/devicetypes.py:113 dcim/views.py:942 dcim/views.py:1181 +#: dcim/views.py:1867 netbox/navigation/menu.py:85 +#: templates/dcim/device/base.html:25 templates/dcim/device_list.html:15 +#: templates/dcim/devicetype/base.html:22 templates/dcim/module.html:22 +#: templates/dcim/moduletype/base.html:22 +msgid "Console Ports" +msgstr "" + +#: dcim/tables/devicetypes.py:116 dcim/views.py:957 dcim/views.py:1196 +#: dcim/views.py:1882 netbox/navigation/menu.py:86 +#: templates/dcim/device/base.html:28 templates/dcim/device_list.html:22 +#: templates/dcim/devicetype/base.html:25 templates/dcim/module.html:25 +#: templates/dcim/moduletype/base.html:25 +msgid "Console Server Ports" +msgstr "" + +#: dcim/tables/devicetypes.py:119 dcim/views.py:972 dcim/views.py:1211 +#: dcim/views.py:1897 netbox/navigation/menu.py:87 +#: templates/dcim/device/base.html:31 templates/dcim/device_list.html:29 +#: templates/dcim/devicetype/base.html:28 templates/dcim/module.html:28 +#: templates/dcim/moduletype/base.html:28 +msgid "Power Ports" +msgstr "" + +#: dcim/tables/devicetypes.py:122 dcim/views.py:987 dcim/views.py:1226 +#: dcim/views.py:1912 netbox/navigation/menu.py:88 +#: templates/dcim/device/base.html:34 templates/dcim/device_list.html:36 +#: templates/dcim/devicetype/base.html:31 templates/dcim/module.html:31 +#: templates/dcim/moduletype/base.html:31 +msgid "Power Outlets" +msgstr "" + +#: dcim/tables/devicetypes.py:128 dcim/views.py:1017 dcim/views.py:1256 +#: dcim/views.py:1948 netbox/navigation/menu.py:83 +#: templates/dcim/device/base.html:40 templates/dcim/devicetype/base.html:37 +#: templates/dcim/module.html:37 templates/dcim/moduletype/base.html:37 +msgid "Front Ports" +msgstr "" + +#: dcim/tables/devicetypes.py:131 dcim/views.py:1032 dcim/views.py:1271 +#: dcim/views.py:1963 netbox/navigation/menu.py:84 +#: templates/dcim/device/base.html:43 templates/dcim/device_list.html:50 +#: templates/dcim/devicetype/base.html:40 templates/dcim/module.html:40 +#: templates/dcim/moduletype/base.html:40 +msgid "Rear Ports" +msgstr "" + +#: dcim/tables/devicetypes.py:134 dcim/views.py:1062 dcim/views.py:2001 +#: netbox/navigation/menu.py:90 templates/dcim/device/base.html:49 +#: templates/dcim/device_list.html:57 templates/dcim/devicetype/base.html:46 +msgid "Device Bays" +msgstr "" + +#: dcim/tables/devicetypes.py:137 dcim/views.py:1047 dcim/views.py:1982 +#: netbox/navigation/menu.py:89 templates/dcim/device/base.html:46 +#: templates/dcim/device_list.html:64 templates/dcim/devicetype/base.html:43 +msgid "Module Bays" +msgstr "" + +#: dcim/tables/power.py:36 netbox/navigation/menu.py:263 +#: templates/dcim/powerpanel.html:53 templates/extras/configrevision.html:59 +msgid "Power Feeds" +msgstr "" + +#: dcim/tables/power.py:80 templates/dcim/powerfeed.html:106 +msgid "Max Utilization" +msgstr "" + +#: dcim/tables/power.py:84 +msgid "Available Power (VA)" +msgstr "" + +#: dcim/tables/racks.py:29 dcim/tables/sites.py:138 +#: netbox/navigation/menu.py:25 netbox/navigation/menu.py:27 +msgid "Racks" +msgstr "" + +#: dcim/tables/racks.py:73 templates/dcim/device.html:340 +#: templates/dcim/rack.html:102 +msgid "Height" +msgstr "" + +#: dcim/tables/racks.py:85 +msgid "Space" +msgstr "" + +#: dcim/tables/racks.py:96 templates/dcim/rack.html:112 +msgid "Outer Width" +msgstr "" + +#: dcim/tables/racks.py:100 templates/dcim/rack.html:122 +msgid "Outer Depth" +msgstr "" + +#: dcim/tables/racks.py:108 +msgid "Max Weight" +msgstr "" + +#: dcim/tables/sites.py:30 dcim/tables/sites.py:57 +#: extras/forms/filtersets.py:334 extras/forms/model_forms.py:291 +#: ipam/forms/bulk_edit.py:130 ipam/forms/model_forms.py:154 +#: ipam/tables/asn.py:65 netbox/navigation/menu.py:16 +#: netbox/navigation/menu.py:18 +msgid "Sites" +msgstr "" + +#: dcim/views.py:131 +#, python-brace-format +msgid "Disconnected {count} {type}" +msgstr "" + +#: dcim/views.py:692 netbox/navigation/menu.py:29 +msgid "Reservations" +msgstr "" + +#: dcim/views.py:711 +msgid "Non-Racked Devices" +msgstr "" + +#: dcim/views.py:2033 extras/forms/model_forms.py:351 +#: templates/extras/configcontext.html:10 +#: virtualization/forms/model_forms.py:226 virtualization/views.py:386 +msgid "Config Context" +msgstr "" + +#: dcim/views.py:2043 virtualization/views.py:396 +msgid "Render Config" +msgstr "" + +#: extras/choices.py:27 extras/forms/misc.py:14 +msgid "Text" +msgstr "" + +#: extras/choices.py:28 +msgid "Text (long)" +msgstr "" + +#: extras/choices.py:29 +msgid "Integer" +msgstr "" + +#: extras/choices.py:30 +msgid "Decimal" +msgstr "" + +#: extras/choices.py:31 +msgid "Boolean (true/false)" +msgstr "" + +#: extras/choices.py:32 +msgid "Date" +msgstr "" + +#: extras/choices.py:33 +msgid "Date & time" +msgstr "" + +#: extras/choices.py:35 +msgid "JSON" +msgstr "" + +#: extras/choices.py:36 +msgid "Selection" +msgstr "" + +#: extras/choices.py:37 +msgid "Multiple selection" +msgstr "" + +#: extras/choices.py:39 +msgid "Multiple objects" +msgstr "" + +#: extras/choices.py:50 templates/extras/customfield.html:69 +#: wireless/choices.py:27 +msgid "Disabled" +msgstr "" + +#: extras/choices.py:51 +msgid "Loose" +msgstr "" + +#: extras/choices.py:52 +msgid "Exact" +msgstr "" + +#: extras/choices.py:64 +msgid "Read/write" +msgstr "" + +#: extras/choices.py:65 +msgid "Read-only" +msgstr "" + +#: extras/choices.py:66 +msgid "Hidden" +msgstr "" + +#: extras/choices.py:67 +msgid "Hidden (if unset)" +msgstr "" + +#: extras/choices.py:94 templates/tenancy/contact.html:58 +#: tenancy/forms/bulk_edit.py:117 wireless/forms/model_forms.py:159 +msgid "Link" +msgstr "" + +#: extras/choices.py:108 +msgid "Newest" +msgstr "" + +#: extras/choices.py:109 +msgid "Oldest" +msgstr "" + +#: extras/choices.py:125 templates/generic/object.html:51 +msgid "Updated" +msgstr "" + +#: extras/choices.py:126 +msgid "Deleted" +msgstr "" + +#: extras/choices.py:143 extras/choices.py:165 +msgid "Info" +msgstr "" + +#: extras/choices.py:144 extras/choices.py:164 +msgid "Success" +msgstr "" + +#: extras/choices.py:145 extras/choices.py:166 +msgid "Warning" +msgstr "" + +#: extras/choices.py:146 +msgid "Danger" +msgstr "" + +#: extras/choices.py:163 utilities/choices.py:190 +msgid "Default" +msgstr "" + +#: extras/choices.py:167 +msgid "Failure" +msgstr "" + +#: extras/choices.py:174 +msgid "Hourly" +msgstr "" + +#: extras/choices.py:175 +msgid "12 hours" +msgstr "" + +#: extras/choices.py:176 +msgid "Daily" +msgstr "" + +#: extras/choices.py:177 +msgid "Weekly" +msgstr "" + +#: extras/choices.py:178 +msgid "30 days" +msgstr "" + +#: extras/choices.py:243 extras/tables/tables.py:283 +#: templates/dcim/virtualchassis_edit.html:108 templates/extras/webhook.html:33 +#: templates/generic/bulk_add_component.html:56 +#: templates/generic/object_edit.html:29 templates/generic/object_edit.html:70 +#: templates/ipam/inc/ipaddress_edit_header.html:10 +msgid "Create" +msgstr "" + +#: extras/choices.py:244 extras/tables/tables.py:286 +#: templates/extras/webhook.html:37 +msgid "Update" +msgstr "" + +#: extras/choices.py:245 extras/tables/tables.py:289 +#: templates/circuits/inc/circuit_termination.html:22 +#: templates/dcim/devicetype/component_templates.html:24 +#: templates/dcim/inc/panels/inventory_items.html:29 +#: templates/dcim/moduletype/component_templates.html:24 +#: templates/dcim/powerpanel.html:71 templates/extras/report_list.html:34 +#: templates/extras/script_list.html:33 templates/extras/webhook.html:41 +#: templates/generic/bulk_delete.html:18 templates/generic/bulk_delete.html:45 +#: templates/generic/object_delete.html:15 templates/htmx/delete_form.html:23 +#: templates/ipam/inc/panels/fhrp_groups.html:35 +#: templates/users/objectpermission.html:49 +#: utilities/templates/buttons/delete.html:9 +msgid "Delete" +msgstr "" + +#: extras/choices.py:269 utilities/choices.py:143 utilities/choices.py:191 +msgid "Blue" +msgstr "" + +#: extras/choices.py:270 utilities/choices.py:142 utilities/choices.py:192 +msgid "Indigo" +msgstr "" + +#: extras/choices.py:271 utilities/choices.py:140 utilities/choices.py:193 +msgid "Purple" +msgstr "" + +#: extras/choices.py:272 utilities/choices.py:137 utilities/choices.py:194 +msgid "Pink" +msgstr "" + +#: extras/choices.py:273 utilities/choices.py:136 utilities/choices.py:195 +msgid "Red" +msgstr "" + +#: extras/choices.py:274 utilities/choices.py:154 utilities/choices.py:196 +msgid "Orange" +msgstr "" + +#: extras/choices.py:275 utilities/choices.py:152 utilities/choices.py:197 +msgid "Yellow" +msgstr "" + +#: extras/choices.py:276 utilities/choices.py:149 utilities/choices.py:198 +msgid "Green" +msgstr "" + +#: extras/choices.py:277 utilities/choices.py:146 utilities/choices.py:199 +msgid "Teal" +msgstr "" + +#: extras/choices.py:278 utilities/choices.py:145 utilities/choices.py:200 +msgid "Cyan" +msgstr "" + +#: extras/choices.py:279 utilities/choices.py:201 +msgid "Gray" +msgstr "" + +#: extras/choices.py:280 utilities/choices.py:160 utilities/choices.py:202 +msgid "Black" +msgstr "" + +#: extras/choices.py:281 utilities/choices.py:161 utilities/choices.py:203 +msgid "White" +msgstr "" + +#: extras/dashboard/forms.py:38 +msgid "Widget type" +msgstr "" + +#: extras/dashboard/widgets.py:146 +msgid "Note" +msgstr "" + +#: extras/dashboard/widgets.py:147 +msgid "Display some arbitrary custom content. Markdown is supported." +msgstr "" + +#: extras/dashboard/widgets.py:160 +msgid "Object Counts" +msgstr "" + +#: extras/dashboard/widgets.py:161 +msgid "" +"Display a set of NetBox models and the number of objects created for each " +"type." +msgstr "" + +#: extras/dashboard/widgets.py:171 +msgid "Filters to apply when counting the number of objects" +msgstr "" + +#: extras/dashboard/widgets.py:207 +msgid "Object List" +msgstr "" + +#: extras/dashboard/widgets.py:208 +msgid "Display an arbitrary list of objects." +msgstr "" + +#: extras/dashboard/widgets.py:221 +msgid "The default number of objects to display" +msgstr "" + +#: extras/dashboard/widgets.py:268 +msgid "RSS Feed" +msgstr "" + +#: extras/dashboard/widgets.py:273 +msgid "Embed an RSS feed from an external website." +msgstr "" + +#: extras/dashboard/widgets.py:280 +msgid "Feed URL" +msgstr "" + +#: extras/dashboard/widgets.py:285 +msgid "The maximum number of objects to display" +msgstr "" + +#: extras/dashboard/widgets.py:290 +msgid "How long to stored the cached content (in seconds)" +msgstr "" + +#: extras/dashboard/widgets.py:342 templates/account/base.html:10 +#: templates/account/bookmarks.html:7 templates/inc/profile_button.html:29 +msgid "Bookmarks" +msgstr "" + +#: extras/dashboard/widgets.py:346 +msgid "Show your personal bookmarks" +msgstr "" + +#: extras/filtersets.py:176 extras/filtersets.py:511 extras/filtersets.py:539 +msgid "Data file (ID)" +msgstr "" + +#: extras/filtersets.py:448 virtualization/forms/filtersets.py:111 +msgid "Cluster type" +msgstr "" + +#: extras/filtersets.py:454 virtualization/filtersets.py:93 +#: virtualization/filtersets.py:143 +msgid "Cluster type (slug)" +msgstr "" + +#: extras/filtersets.py:459 ipam/forms/bulk_edit.py:477 +#: ipam/forms/model_forms.py:587 virtualization/forms/filtersets.py:105 +msgid "Cluster group" +msgstr "" + +#: extras/filtersets.py:465 virtualization/filtersets.py:132 +msgid "Cluster group (slug)" +msgstr "" + +#: extras/filtersets.py:475 tenancy/forms/forms.py:16 tenancy/forms/forms.py:39 +msgid "Tenant group" +msgstr "" + +#: extras/filtersets.py:481 tenancy/filtersets.py:151 tenancy/filtersets.py:171 +msgid "Tenant group (slug)" +msgstr "" + +#: extras/filtersets.py:497 templates/extras/tag.html:12 +msgid "Tag" +msgstr "" + +#: extras/filtersets.py:503 +msgid "Tag (slug)" +msgstr "" + +#: extras/filtersets.py:563 extras/forms/filtersets.py:413 +msgid "Has local config context data" +msgstr "" + +#: extras/filtersets.py:588 +msgid "User name" +msgstr "" + +#: extras/forms/bulk_edit.py:31 extras/forms/filtersets.py:58 +msgid "Group name" +msgstr "" + +#: extras/forms/bulk_edit.py:39 extras/forms/filtersets.py:66 +#: extras/tables/tables.py:72 templates/extras/customfield.html:39 +#: templates/generic/bulk_import.html:116 +msgid "Required" +msgstr "" + +#: extras/forms/bulk_edit.py:52 extras/forms/bulk_import.py:56 +#: extras/forms/filtersets.py:80 extras/models/customfields.py:187 +msgid "UI visibility" +msgstr "" + +#: extras/forms/bulk_edit.py:58 extras/forms/filtersets.py:83 +msgid "Is cloneable" +msgstr "" + +#: extras/forms/bulk_edit.py:97 extras/forms/filtersets.py:123 +msgid "New window" +msgstr "" + +#: extras/forms/bulk_edit.py:106 +msgid "Button class" +msgstr "" + +#: extras/forms/bulk_edit.py:123 extras/forms/filtersets.py:161 +#: extras/models/models.py:356 +msgid "MIME type" +msgstr "" + +#: extras/forms/bulk_edit.py:128 extras/forms/filtersets.py:164 +msgid "File extension" +msgstr "" + +#: extras/forms/bulk_edit.py:133 extras/forms/filtersets.py:168 +msgid "As attachment" +msgstr "" + +#: extras/forms/bulk_edit.py:161 extras/forms/filtersets.py:210 +#: extras/tables/tables.py:236 templates/extras/savedfilter.html:30 +msgid "Shared" +msgstr "" + +#: extras/forms/bulk_edit.py:182 +msgid "On create" +msgstr "" + +#: extras/forms/bulk_edit.py:187 +msgid "On update" +msgstr "" + +#: extras/forms/bulk_edit.py:192 +msgid "On delete" +msgstr "" + +#: extras/forms/bulk_edit.py:197 +msgid "On job start" +msgstr "" + +#: extras/forms/bulk_edit.py:202 +msgid "On job end" +msgstr "" + +#: extras/forms/bulk_edit.py:209 extras/forms/filtersets.py:239 +#: extras/models/models.py:100 +msgid "HTTP method" +msgstr "" + +#: extras/forms/bulk_edit.py:213 templates/extras/webhook.html:66 +msgid "Payload URL" +msgstr "" + +#: extras/forms/bulk_edit.py:218 extras/models/models.py:146 +msgid "SSL verification" +msgstr "" + +#: extras/forms/bulk_edit.py:221 templates/extras/webhook.html:74 +msgid "Secret" +msgstr "" + +#: extras/forms/bulk_edit.py:226 +msgid "CA file path" +msgstr "" + +#: extras/forms/bulk_edit.py:261 +msgid "Is active" +msgstr "" + +#: extras/forms/bulk_import.py:31 extras/forms/bulk_import.py:91 +#: extras/forms/bulk_import.py:107 extras/forms/bulk_import.py:131 +#: extras/forms/bulk_import.py:145 extras/forms/filtersets.py:111 +#: extras/forms/filtersets.py:157 extras/forms/filtersets.py:198 +#: extras/forms/model_forms.py:46 extras/forms/model_forms.py:119 +#: extras/forms/model_forms.py:147 extras/forms/model_forms.py:189 +#: extras/forms/model_forms.py:227 +msgid "Content types" +msgstr "" + +#: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:94 +#: extras/forms/bulk_import.py:110 extras/forms/bulk_import.py:133 +#: extras/forms/bulk_import.py:148 tenancy/forms/bulk_import.py:96 +msgid "One or more assigned object types" +msgstr "" + +#: extras/forms/bulk_import.py:39 +msgid "Field data type (e.g. text, integer, etc.)" +msgstr "" + +#: extras/forms/bulk_import.py:42 extras/forms/filtersets.py:50 +#: extras/forms/filtersets.py:234 extras/forms/model_forms.py:51 +#: extras/forms/model_forms.py:215 tenancy/forms/filtersets.py:93 +msgid "Object type" +msgstr "" + +#: extras/forms/bulk_import.py:46 +msgid "Object type (for object or multi-object fields)" +msgstr "" + +#: extras/forms/bulk_import.py:49 extras/forms/filtersets.py:75 +msgid "Choice set" +msgstr "" + +#: extras/forms/bulk_import.py:53 +msgid "Choice set (for selection fields)" +msgstr "" + +#: extras/forms/bulk_import.py:58 +msgid "How the custom field is displayed in the user interface" +msgstr "" + +#: extras/forms/bulk_import.py:74 +msgid "The base set of predefined choices to use (if any)" +msgstr "" + +#: extras/forms/bulk_import.py:79 +msgid "Comma-separated list of field choices" +msgstr "" + +#: extras/forms/bulk_import.py:174 +msgid "Assigned object type" +msgstr "" + +#: extras/forms/bulk_import.py:179 +msgid "The classification of entry" +msgstr "" + +#: extras/forms/filtersets.py:55 +msgid "Field type" +msgstr "" + +#: extras/forms/filtersets.py:94 extras/tables/tables.py:87 +#: templates/generic/bulk_import.html:148 +msgid "Choices" +msgstr "" + +#: extras/forms/filtersets.py:138 extras/forms/filtersets.py:302 +#: extras/forms/filtersets.py:392 extras/forms/model_forms.py:346 +#: templates/core/job.html:80 templates/extras/configcontext.html:86 +msgid "Data" +msgstr "" + +#: extras/forms/filtersets.py:149 extras/forms/filtersets.py:316 +#: extras/forms/filtersets.py:402 utilities/choices.py:219 +#: utilities/forms/bulk_import.py:27 +msgid "Data file" +msgstr "" + +#: extras/forms/filtersets.py:182 +msgid "Content type" +msgstr "" + +#: extras/forms/filtersets.py:229 extras/forms/model_forms.py:234 +#: templates/extras/webhook.html:28 +msgid "Events" +msgstr "" + +#: extras/forms/filtersets.py:253 +msgid "Object creations" +msgstr "" + +#: extras/forms/filtersets.py:260 +msgid "Object updates" +msgstr "" + +#: extras/forms/filtersets.py:267 +msgid "Object deletions" +msgstr "" + +#: extras/forms/filtersets.py:274 +msgid "Job starts" +msgstr "" + +#: extras/forms/filtersets.py:281 extras/forms/model_forms.py:250 +msgid "Job terminations" +msgstr "" + +#: extras/forms/filtersets.py:290 +msgid "Tagged object type" +msgstr "" + +#: extras/forms/filtersets.py:295 +msgid "Allowed object type" +msgstr "" + +#: extras/forms/filtersets.py:324 extras/forms/model_forms.py:281 +#: netbox/navigation/menu.py:19 +msgid "Regions" +msgstr "" + +#: extras/forms/filtersets.py:329 extras/forms/model_forms.py:286 +msgid "Site groups" +msgstr "" + +#: extras/forms/filtersets.py:339 extras/forms/model_forms.py:296 +#: netbox/navigation/menu.py:21 +msgid "Locations" +msgstr "" + +#: extras/forms/filtersets.py:344 extras/forms/model_forms.py:301 +msgid "Device types" +msgstr "" + +#: extras/forms/filtersets.py:349 extras/forms/model_forms.py:306 +msgid "Roles" +msgstr "" + +#: extras/forms/filtersets.py:359 extras/forms/model_forms.py:316 +msgid "Cluster types" +msgstr "" + +#: extras/forms/filtersets.py:365 extras/forms/model_forms.py:321 +msgid "Cluster groups" +msgstr "" + +#: extras/forms/filtersets.py:370 extras/forms/model_forms.py:326 +#: netbox/navigation/menu.py:224 netbox/navigation/menu.py:226 +#: templates/virtualization/clustertype.html:33 +#: virtualization/tables/clusters.py:23 virtualization/tables/clusters.py:45 +msgid "Clusters" +msgstr "" + +#: extras/forms/filtersets.py:375 extras/forms/model_forms.py:331 +msgid "Tenant groups" +msgstr "" + +#: extras/forms/filtersets.py:429 extras/forms/filtersets.py:470 +msgid "After" +msgstr "" + +#: extras/forms/filtersets.py:434 extras/forms/filtersets.py:475 +msgid "Before" +msgstr "" + +#: extras/forms/filtersets.py:465 extras/tables/tables.py:426 +#: templates/extras/htmx/report_result.html:43 +#: templates/extras/objectchange.html:34 +msgid "Time" +msgstr "" + +#: extras/forms/filtersets.py:479 extras/tables/tables.py:440 +#: templates/extras/objectchange.html:50 +msgid "Action" +msgstr "" + +#: extras/forms/mixins.py:71 extras/forms/model_forms.py:195 +#: templates/extras/savedfilter.html:10 +msgid "Saved Filter" +msgstr "" + +#: extras/forms/model_forms.py:56 +msgid "Type of the related object (for object/multi-object fields only)" +msgstr "" + +#: extras/forms/model_forms.py:64 templates/extras/customfield.html:11 +msgid "Custom Field" +msgstr "" + +#: extras/forms/model_forms.py:67 templates/extras/customfield.html:60 +msgid "Behavior" +msgstr "" + +#: extras/forms/model_forms.py:68 +msgid "Values" +msgstr "" + +#: extras/forms/model_forms.py:69 extras/forms/model_forms.py:494 +#: templates/extras/configrevision.html:147 +msgid "Validation" +msgstr "" + +#: extras/forms/model_forms.py:77 +msgid "" +"The type of data stored in this field. For object/multi-object fields, " +"select the related object type below." +msgstr "" + +#: extras/forms/model_forms.py:80 +msgid "" +"This will be displayed as help text for the form field. Markdown is " +"supported." +msgstr "" + +#: extras/forms/model_forms.py:97 +msgid "" +"Enter one choice per line. An optional label may be specified for each " +"choice by appending it with a comma. Example:" +msgstr "" + +#: extras/forms/model_forms.py:125 templates/extras/customlink.html:10 +msgid "Custom Link" +msgstr "" + +#: extras/forms/model_forms.py:126 +msgid "Templates" +msgstr "" + +#: extras/forms/model_forms.py:138 +msgid "" +"Jinja2 template code for the link text. Reference the object as " +"{{ object }}. Links which render as empty text will not be " +"displayed." +msgstr "" + +#: extras/forms/model_forms.py:141 +msgid "" +"Jinja2 template code for the link URL. Reference the object as " +"{{ object }}." +msgstr "" + +#: extras/forms/model_forms.py:152 extras/forms/model_forms.py:397 +msgid "Template code" +msgstr "" + +#: extras/forms/model_forms.py:158 templates/extras/exporttemplate.html:17 +msgid "Export Template" +msgstr "" + +#: extras/forms/model_forms.py:160 +msgid "Rendering" +msgstr "" + +#: extras/forms/model_forms.py:174 extras/forms/model_forms.py:422 +msgid "Template content is populated from the remote source selected below." +msgstr "" + +#: extras/forms/model_forms.py:181 extras/forms/model_forms.py:429 +msgid "Must specify either local content or a data file" +msgstr "" + +#: extras/forms/model_forms.py:233 templates/extras/webhook.html:11 +msgid "Webhook" +msgstr "" + +#: extras/forms/model_forms.py:235 templates/extras/webhook.html:57 +msgid "HTTP Request" +msgstr "" + +#: extras/forms/model_forms.py:238 templates/extras/webhook.html:116 +msgid "Conditions" +msgstr "" + +#: extras/forms/model_forms.py:239 templates/extras/webhook.html:82 +msgid "SSL" +msgstr "" + +#: extras/forms/model_forms.py:246 +msgid "Creations" +msgstr "" + +#: extras/forms/model_forms.py:247 +msgid "Updates" +msgstr "" + +#: extras/forms/model_forms.py:248 +msgid "Deletions" +msgstr "" + +#: extras/forms/model_forms.py:249 +msgid "Job executions" +msgstr "" + +#: extras/forms/model_forms.py:262 users/forms/model_forms.py:285 +msgid "Object types" +msgstr "" + +#: extras/forms/model_forms.py:336 netbox/navigation/menu.py:40 +#: tenancy/tables/tenants.py:22 +msgid "Tenants" +msgstr "" + +#: extras/forms/model_forms.py:353 ipam/forms/filtersets.py:145 +#: templates/extras/configcontext.html:62 templates/ipam/ipaddress.html:62 +#: templates/ipam/vlan_edit.html:30 tenancy/forms/filtersets.py:87 +#: users/forms/model_forms.py:323 +msgid "Assignment" +msgstr "" + +#: extras/forms/model_forms.py:379 +msgid "Data is populated from the remote source selected below." +msgstr "" + +#: extras/forms/model_forms.py:385 +msgid "Must specify either local data or a data file" +msgstr "" + +#: extras/forms/model_forms.py:404 templates/core/datafile.html:65 +msgid "Content" +msgstr "" + +#: extras/forms/model_forms.py:488 templates/dcim/rack_elevation_list.html:6 +#: templates/extras/configrevision.html:43 +msgid "Rack Elevations" +msgstr "" + +#: extras/forms/model_forms.py:490 netbox/navigation/menu.py:142 +#: templates/extras/configrevision.html:79 +msgid "IPAM" +msgstr "" + +#: extras/forms/model_forms.py:491 templates/extras/configrevision.html:95 +msgid "Security" +msgstr "" + +#: extras/forms/model_forms.py:492 templates/extras/configrevision.html:107 +msgid "Banners" +msgstr "" + +#: extras/forms/model_forms.py:493 templates/extras/configrevision.html:131 +msgid "Pagination" +msgstr "" + +#: extras/forms/model_forms.py:495 templates/account/preferences.html:6 +#: templates/extras/configrevision.html:159 +msgid "User Preferences" +msgstr "" + +#: extras/forms/model_forms.py:499 +msgid "Config Revision" +msgstr "" + +#: extras/forms/model_forms.py:537 +msgid "This parameter has been defined statically and cannot be modified." +msgstr "" + +#: extras/forms/model_forms.py:545 +#, python-brace-format +msgid "Current value: {value}" +msgstr "" + +#: extras/forms/model_forms.py:547 +msgid " (default)" +msgstr "" + +#: extras/forms/reports.py:18 extras/forms/scripts.py:24 +msgid "Schedule at" +msgstr "" + +#: extras/forms/reports.py:19 +msgid "Schedule execution of report to a set time" +msgstr "" + +#: extras/forms/reports.py:24 extras/forms/scripts.py:30 +msgid "Recurs every" +msgstr "" + +#: extras/forms/reports.py:28 +msgid "Interval at which this report is re-run (in minutes)" +msgstr "" + +#: extras/forms/reports.py:36 extras/forms/scripts.py:42 +#, python-brace-format +msgid " (current time: {now})" +msgstr "" + +#: extras/forms/reports.py:46 extras/forms/scripts.py:52 +msgid "Scheduled time must be in the future." +msgstr "" + +#: extras/forms/scripts.py:18 +msgid "Commit changes" +msgstr "" + +#: extras/forms/scripts.py:19 +msgid "Commit changes to the database (uncheck for a dry-run)" +msgstr "" + +#: extras/forms/scripts.py:25 +msgid "Schedule execution of script to a set time" +msgstr "" + +#: extras/forms/scripts.py:34 +msgid "Interval at which this script is re-run (in minutes)" +msgstr "" + +#: extras/models/change_logging.py:23 +msgid "time" +msgstr "" + +#: extras/models/change_logging.py:36 +msgid "user name" +msgstr "" + +#: extras/models/change_logging.py:41 +msgid "request ID" +msgstr "" + +#: extras/models/change_logging.py:46 extras/models/staging.py:69 +msgid "action" +msgstr "" + +#: extras/models/change_logging.py:80 +msgid "pre-change data" +msgstr "" + +#: extras/models/change_logging.py:86 +msgid "post-change data" +msgstr "" + +#: extras/models/change_logging.py:96 +msgid "object change" +msgstr "" + +#: extras/models/change_logging.py:97 +msgid "object changes" +msgstr "" + +#: extras/models/configs.py:130 +msgid "config context" +msgstr "" + +#: extras/models/configs.py:131 +msgid "config contexts" +msgstr "" + +#: extras/models/configs.py:149 extras/models/configs.py:205 +msgid "JSON data must be in object form. Example:" +msgstr "" + +#: extras/models/configs.py:169 +msgid "" +"Local config context data takes precedence over source contexts in the final " +"rendered config context" +msgstr "" + +#: extras/models/configs.py:224 +msgid "template code" +msgstr "" + +#: extras/models/configs.py:225 +msgid "Jinja2 template code." +msgstr "" + +#: extras/models/configs.py:228 +msgid "environment parameters" +msgstr "" + +#: extras/models/configs.py:233 +msgid "" +"Any additional parameters to pass when constructing the Jinja2 " +"environment." +msgstr "" + +#: extras/models/configs.py:240 +msgid "config template" +msgstr "" + +#: extras/models/configs.py:241 +msgid "config templates" +msgstr "" + +#: extras/models/customfields.py:66 +msgid "The object(s) to which this field applies." +msgstr "" + +#: extras/models/customfields.py:73 +msgid "The type of data this custom field holds" +msgstr "" + +#: extras/models/customfields.py:80 +msgid "The type of NetBox object this field maps to (for object fields)" +msgstr "" + +#: extras/models/customfields.py:86 +msgid "Internal field name" +msgstr "" + +#: extras/models/customfields.py:90 +msgid "Only alphanumeric characters and underscores are allowed." +msgstr "" + +#: extras/models/customfields.py:95 +msgid "Double underscores are not permitted in custom field names." +msgstr "" + +#: extras/models/customfields.py:106 +msgid "" +"Name of the field as displayed to users (if not provided, 'the field's name " +"will be used)" +msgstr "" + +#: extras/models/customfields.py:110 extras/models/models.py:264 +msgid "group name" +msgstr "" + +#: extras/models/customfields.py:113 +msgid "Custom fields within the same group will be displayed together" +msgstr "" + +#: extras/models/customfields.py:121 +msgid "required" +msgstr "" + +#: extras/models/customfields.py:123 +msgid "" +"If true, this field is required when creating new objects or editing an " +"existing object." +msgstr "" + +#: extras/models/customfields.py:126 +msgid "search weight" +msgstr "" + +#: extras/models/customfields.py:129 +msgid "" +"Weighting for search. Lower values are considered more important. Fields " +"with a search weight of zero will be ignored." +msgstr "" + +#: extras/models/customfields.py:134 +msgid "filter logic" +msgstr "" + +#: extras/models/customfields.py:138 +msgid "" +"Loose matches any instance of a given string; exact matches the entire field." +msgstr "" + +#: extras/models/customfields.py:141 +msgid "default" +msgstr "" + +#: extras/models/customfields.py:145 +msgid "" +"Default value for the field (must be a JSON value). Encapsulate strings with " +"double quotes (e.g. \"Foo\")." +msgstr "" + +#: extras/models/customfields.py:150 +msgid "display weight" +msgstr "" + +#: extras/models/customfields.py:151 +msgid "Fields with higher weights appear lower in a form." +msgstr "" + +#: extras/models/customfields.py:156 +msgid "minimum value" +msgstr "" + +#: extras/models/customfields.py:157 +msgid "Minimum allowed value (for numeric fields)" +msgstr "" + +#: extras/models/customfields.py:162 +msgid "maximum value" +msgstr "" + +#: extras/models/customfields.py:163 +msgid "Maximum allowed value (for numeric fields)" +msgstr "" + +#: extras/models/customfields.py:169 +msgid "validation regex" +msgstr "" + +#: extras/models/customfields.py:171 +#, python-brace-format +msgid "" +"Regular expression to enforce on text field values. Use ^ and $ to force " +"matching of entire string. For example, ^[A-Z]{3}$ will limit " +"values to exactly three uppercase letters." +msgstr "" + +#: extras/models/customfields.py:179 +msgid "choice set" +msgstr "" + +#: extras/models/customfields.py:188 +msgid "Specifies the visibility of custom field in the UI" +msgstr "" + +#: extras/models/customfields.py:192 +msgid "is cloneable" +msgstr "" + +#: extras/models/customfields.py:193 +msgid "Replicate this value when cloning objects" +msgstr "" + +#: extras/models/customfields.py:206 +msgid "custom field" +msgstr "" + +#: extras/models/customfields.py:207 +msgid "custom fields" +msgstr "" + +#: extras/models/customfields.py:290 +#, python-brace-format +msgid "Invalid default value \"{value}\": {error}" +msgstr "" + +#: extras/models/customfields.py:297 +msgid "A minimum value may be set only for numeric fields" +msgstr "" + +#: extras/models/customfields.py:299 +msgid "A maximum value may be set only for numeric fields" +msgstr "" + +#: extras/models/customfields.py:309 +msgid "Regular expression validation is supported only for text and URL fields" +msgstr "" + +#: extras/models/customfields.py:319 +msgid "Selection fields must specify a set of choices." +msgstr "" + +#: extras/models/customfields.py:323 +msgid "Choices may be set only on selection fields." +msgstr "" + +#: extras/models/customfields.py:330 +msgid "Object fields must define an object type." +msgstr "" + +#: extras/models/customfields.py:335 +#, python-brace-format +msgid "{type} fields may not define an object type." +msgstr "" + +#: extras/models/customfields.py:415 +msgid "True" +msgstr "" + +#: extras/models/customfields.py:416 +msgid "False" +msgstr "" + +#: extras/models/customfields.py:498 +#, python-brace-format +msgid "Values must match this regex: {regex}" +msgstr "" + +#: extras/models/customfields.py:513 +msgid "Field is set to read-only." +msgstr "" + +#: extras/models/customfields.py:595 +msgid "Value must be a string." +msgstr "" + +#: extras/models/customfields.py:597 +#, python-brace-format +msgid "Value must match regex '{regex}'" +msgstr "" + +#: extras/models/customfields.py:602 +msgid "Value must be an integer." +msgstr "" + +#: extras/models/customfields.py:605 extras/models/customfields.py:620 +#, python-brace-format +msgid "Value must be at least {minimum}" +msgstr "" + +#: extras/models/customfields.py:609 extras/models/customfields.py:624 +#, python-brace-format +msgid "Value must not exceed {maximum}" +msgstr "" + +#: extras/models/customfields.py:617 +msgid "Value must be a decimal." +msgstr "" + +#: extras/models/customfields.py:629 +msgid "Value must be true or false." +msgstr "" + +#: extras/models/customfields.py:637 +msgid "Date values must be in ISO 8601 format (YYYY-MM-DD)." +msgstr "" + +#: extras/models/customfields.py:646 +msgid "Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS)." +msgstr "" + +#: extras/models/customfields.py:653 +#, python-brace-format +msgid "Invalid choice ({value}) for choice set {choiceset}." +msgstr "" + +#: extras/models/customfields.py:663 +#, python-brace-format +msgid "Invalid choice(s) ({value}) for choice set {choiceset}." +msgstr "" + +#: extras/models/customfields.py:672 +#, python-brace-format +msgid "Value must be an object ID, not {type}" +msgstr "" + +#: extras/models/customfields.py:678 +#, python-brace-format +msgid "Value must be a list of object IDs, not {type}" +msgstr "" + +#: extras/models/customfields.py:682 +#, python-brace-format +msgid "Found invalid object ID: {id}" +msgstr "" + +#: extras/models/customfields.py:685 +msgid "Required field cannot be empty." +msgstr "" + +#: extras/models/customfields.py:704 +msgid "Base set of predefined choices (optional)" +msgstr "" + +#: extras/models/customfields.py:716 +msgid "Choices are automatically ordered alphabetically" +msgstr "" + +#: extras/models/customfields.py:723 +msgid "custom field choice set" +msgstr "" + +#: extras/models/customfields.py:724 +msgid "custom field choice sets" +msgstr "" + +#: extras/models/customfields.py:760 +msgid "Must define base or extra choices." +msgstr "" + +#: extras/models/dashboard.py:19 +msgid "layout" +msgstr "" + +#: extras/models/dashboard.py:23 +msgid "config" +msgstr "" + +#: extras/models/dashboard.py:28 +msgid "dashboard" +msgstr "" + +#: extras/models/dashboard.py:29 +msgid "dashboards" +msgstr "" + +#: extras/models/models.py:50 +msgid "object types" +msgstr "" + +#: extras/models/models.py:52 +msgid "The object(s) to which this Webhook applies." +msgstr "" + +#: extras/models/models.py:60 +msgid "on create" +msgstr "" + +#: extras/models/models.py:62 +msgid "Triggers when a matching object is created." +msgstr "" + +#: extras/models/models.py:65 +msgid "on update" +msgstr "" + +#: extras/models/models.py:67 +msgid "Triggers when a matching object is updated." +msgstr "" + +#: extras/models/models.py:70 +msgid "on delete" +msgstr "" + +#: extras/models/models.py:72 +msgid "Triggers when a matching object is deleted." +msgstr "" + +#: extras/models/models.py:75 +msgid "on job start" +msgstr "" + +#: extras/models/models.py:77 +msgid "Triggers when a job for a matching object is started." +msgstr "" + +#: extras/models/models.py:80 +msgid "on job end" +msgstr "" + +#: extras/models/models.py:82 +msgid "Triggers when a job for a matching object terminates." +msgstr "" + +#: extras/models/models.py:88 +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 "" + +#: extras/models/models.py:105 +msgid "HTTP content type" +msgstr "" + +#: extras/models/models.py:107 +msgid "" +"The complete list of official content types is available here." +msgstr "" + +#: extras/models/models.py:112 +msgid "additional headers" +msgstr "" + +#: extras/models/models.py:115 +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: " +"Value. Jinja2 template processing is supported with the same context " +"as the request body (below)." +msgstr "" + +#: extras/models/models.py:121 +msgid "body template" +msgstr "" + +#: extras/models/models.py:124 +msgid "" +"Jinja2 template for a custom request body. If blank, a JSON object " +"representing the change will be included. Available context data includes: " +"event, model, timestamp, " +"username, request_id, and data." +msgstr "" + +#: extras/models/models.py:130 +msgid "secret" +msgstr "" + +#: extras/models/models.py:134 +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 "" + +#: extras/models/models.py:139 +msgid "conditions" +msgstr "" + +#: extras/models/models.py:142 +msgid "" +"A set of conditions which determine whether the webhook will be generated." +msgstr "" + +#: extras/models/models.py:147 +msgid "Enable SSL certificate verification. Disable with caution!" +msgstr "" + +#: extras/models/models.py:153 templates/extras/webhook.html:91 +msgid "CA File Path" +msgstr "" + +#: extras/models/models.py:155 +msgid "" +"The specific CA certificate file to use for SSL verification. Leave blank to " +"use the system defaults." +msgstr "" + +#: extras/models/models.py:167 +msgid "webhook" +msgstr "" + +#: extras/models/models.py:168 +msgid "webhooks" +msgstr "" + +#: extras/models/models.py:188 +msgid "" +"At least one event type must be selected: create, update, delete, job_start, " +"and/or job_end." +msgstr "" + +#: extras/models/models.py:200 +msgid "Do not specify a CA certificate file if SSL verification is disabled." +msgstr "" + +#: extras/models/models.py:240 +msgid "The object type(s) to which this link applies." +msgstr "" + +#: extras/models/models.py:252 +msgid "link text" +msgstr "" + +#: extras/models/models.py:253 +msgid "Jinja2 template code for link text" +msgstr "" + +#: extras/models/models.py:256 +msgid "link URL" +msgstr "" + +#: extras/models/models.py:257 +msgid "Jinja2 template code for link URL" +msgstr "" + +#: extras/models/models.py:267 +msgid "Links with the same group will appear as a dropdown menu" +msgstr "" + +#: extras/models/models.py:270 +msgid "button class" +msgstr "" + +#: extras/models/models.py:274 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" + +#: extras/models/models.py:277 +msgid "new window" +msgstr "" + +#: extras/models/models.py:279 +msgid "Force link to open in a new window" +msgstr "" + +#: extras/models/models.py:288 +msgid "custom link" +msgstr "" + +#: extras/models/models.py:289 +msgid "custom links" +msgstr "" + +#: extras/models/models.py:336 +msgid "The object type(s) to which this template applies." +msgstr "" + +#: extras/models/models.py:349 +msgid "" +"Jinja2 template code. The list of objects being exported is passed as a " +"context variable named queryset." +msgstr "" + +#: extras/models/models.py:357 +msgid "Defaults to text/plain; charset=utf-8" +msgstr "" + +#: extras/models/models.py:360 +msgid "file extension" +msgstr "" + +#: extras/models/models.py:363 +msgid "Extension to append to the rendered filename" +msgstr "" + +#: extras/models/models.py:366 +msgid "as attachment" +msgstr "" + +#: extras/models/models.py:368 +msgid "Download file as attachment" +msgstr "" + +#: extras/models/models.py:377 +msgid "export template" +msgstr "" + +#: extras/models/models.py:378 +msgid "export templates" +msgstr "" + +#: extras/models/models.py:395 +#, python-brace-format +msgid "\"{name}\" is a reserved name. Please choose a different name." +msgstr "" + +#: extras/models/models.py:445 +msgid "The object type(s) to which this filter applies." +msgstr "" + +#: extras/models/models.py:477 +msgid "shared" +msgstr "" + +#: extras/models/models.py:490 +msgid "saved filter" +msgstr "" + +#: extras/models/models.py:491 +msgid "saved filters" +msgstr "" + +#: extras/models/models.py:509 +msgid "Filter parameters must be stored as a dictionary of keyword arguments." +msgstr "" + +#: extras/models/models.py:537 +msgid "image height" +msgstr "" + +#: extras/models/models.py:540 +msgid "image width" +msgstr "" + +#: extras/models/models.py:554 +msgid "image attachment" +msgstr "" + +#: extras/models/models.py:555 +msgid "image attachments" +msgstr "" + +#: extras/models/models.py:623 +msgid "kind" +msgstr "" + +#: extras/models/models.py:634 +msgid "journal entry" +msgstr "" + +#: extras/models/models.py:635 +msgid "journal entries" +msgstr "" + +#: extras/models/models.py:651 +#, python-brace-format +msgid "Journaling is not supported for this object type ({type})." +msgstr "" + +#: extras/models/models.py:690 +msgid "bookmark" +msgstr "" + +#: extras/models/models.py:691 +msgid "bookmarks" +msgstr "" + +#: extras/models/models.py:708 +msgid "comment" +msgstr "" + +#: extras/models/models.py:715 +msgid "configuration data" +msgstr "" + +#: extras/models/models.py:722 +msgid "config revision" +msgstr "" + +#: extras/models/models.py:723 +msgid "config revisions" +msgstr "" + +#: extras/models/models.py:727 +msgid "Default configuration" +msgstr "" + +#: extras/models/models.py:729 +msgid "Current configuration" +msgstr "" + +#: extras/models/models.py:730 +#, python-brace-format +msgid "Config revision #{id}" +msgstr "" + +#: extras/models/reports.py:46 +msgid "report module" +msgstr "" + +#: extras/models/reports.py:47 +msgid "report modules" +msgstr "" + +#: extras/models/scripts.py:46 +msgid "script module" +msgstr "" + +#: extras/models/scripts.py:47 +msgid "script modules" +msgstr "" + +#: extras/models/search.py:22 +msgid "timestamp" +msgstr "" + +#: extras/models/search.py:37 +msgid "field" +msgstr "" + +#: extras/models/search.py:45 +msgid "value" +msgstr "" + +#: extras/models/search.py:54 +msgid "cached value" +msgstr "" + +#: extras/models/search.py:55 +msgid "cached values" +msgstr "" + +#: extras/models/staging.py:44 +msgid "branch" +msgstr "" + +#: extras/models/staging.py:45 +msgid "branches" +msgstr "" + +#: extras/models/staging.py:94 +msgid "staged change" +msgstr "" + +#: extras/models/staging.py:95 +msgid "staged changes" +msgstr "" + +#: extras/models/tags.py:44 +msgid "The object type(s) to which this this tag can be applied." +msgstr "" + +#: extras/models/tags.py:53 +msgid "tag" +msgstr "" + +#: extras/models/tags.py:54 +msgid "tags" +msgstr "" + +#: extras/models/tags.py:80 +msgid "tagged item" +msgstr "" + +#: extras/models/tags.py:81 +msgid "tagged items" +msgstr "" + +#: extras/tables/tables.py:48 users/forms/filtersets.py:47 users/tables.py:39 +msgid "Is Active" +msgstr "" + +#: extras/tables/tables.py:69 extras/tables/tables.py:141 +#: extras/tables/tables.py:165 extras/tables/tables.py:230 +#: extras/tables/tables.py:277 +msgid "Content Types" +msgstr "" + +#: extras/tables/tables.py:75 templates/extras/customfield.html:82 +msgid "UI Visibility" +msgstr "" + +#: extras/tables/tables.py:82 templates/extras/customfield.html:48 +msgid "Choice Set" +msgstr "" + +#: extras/tables/tables.py:90 +msgid "Is Cloneable" +msgstr "" + +#: extras/tables/tables.py:120 +msgid "Count" +msgstr "" + +#: extras/tables/tables.py:123 +msgid "Order Alphabetically" +msgstr "" + +#: extras/tables/tables.py:147 templates/extras/customlink.html:34 +msgid "New Window" +msgstr "" + +#: extras/tables/tables.py:168 +msgid "As Attachment" +msgstr "" + +#: extras/tables/tables.py:175 extras/tables/tables.py:367 +#: extras/tables/tables.py:402 templates/core/datafile.html:32 +#: templates/dcim/device/render_config.html:23 +#: templates/extras/configcontext.html:40 +#: templates/extras/configtemplate.html:32 +#: templates/extras/exporttemplate.html:51 +#: templates/generic/bulk_import.html:30 +#: templates/virtualization/virtualmachine/render_config.html:23 +msgid "Data File" +msgstr "" + +#: extras/tables/tables.py:180 extras/tables/tables.py:379 +#: extras/tables/tables.py:407 +msgid "Synced" +msgstr "" + +#: extras/tables/tables.py:200 +msgid "Content Type" +msgstr "" + +#: extras/tables/tables.py:207 +msgid "Image" +msgstr "" + +#: extras/tables/tables.py:212 +msgid "Size (Bytes)" +msgstr "" + +#: extras/tables/tables.py:255 extras/tables/tables.py:326 +#: templates/extras/customfield.html:92 +#: templates/users/objectpermission.html:68 users/tables.py:83 +msgid "Object Types" +msgstr "" + +#: extras/tables/tables.py:292 +msgid "Job Start" +msgstr "" + +#: extras/tables/tables.py:295 +msgid "Job End" +msgstr "" + +#: extras/tables/tables.py:298 +msgid "SSL Validation" +msgstr "" + +#: extras/tables/tables.py:436 templates/account/profile.html:20 +#: templates/users/user.html:22 +msgid "Full Name" +msgstr "" + +#: extras/tables/tables.py:453 templates/extras/objectchange.html:72 +msgid "Request ID" +msgstr "" + +#: extras/tables/tables.py:490 +msgid "Comments (Short)" +msgstr "" + +#: extras/views.py:836 +msgid "Your dashboard has been reset." +msgstr "" + +#: ipam/api/field_serializers.py:17 +msgid "Enter a valid IPv4 or IPv6 address with optional mask." +msgstr "" + +#: ipam/api/field_serializers.py:24 +#, python-brace-format +msgid "Invalid IP address format: {data}" +msgstr "" + +#: ipam/api/field_serializers.py:37 +msgid "Enter a valid IPv4 or IPv6 prefix and mask in CIDR notation." +msgstr "" + +#: ipam/api/field_serializers.py:44 +#, python-brace-format +msgid "Invalid IP prefix format: {data}" +msgstr "" + +#: ipam/choices.py:30 +msgid "Container" +msgstr "" + +#: ipam/choices.py:72 +msgid "DHCP" +msgstr "" + +#: ipam/choices.py:73 +msgid "SLAAC" +msgstr "" + +#: ipam/choices.py:89 +msgid "Loopback" +msgstr "" + +#: ipam/choices.py:90 tenancy/choices.py:18 +msgid "Secondary" +msgstr "" + +#: ipam/choices.py:91 +msgid "Anycast" +msgstr "" + +#: ipam/choices.py:115 +msgid "Standard" +msgstr "" + +#: ipam/choices.py:120 +msgid "CheckPoint" +msgstr "" + +#: ipam/choices.py:123 +msgid "Cisco" +msgstr "" + +#: ipam/choices.py:137 +msgid "Plaintext" +msgstr "" + +#: ipam/filtersets.py:47 ipam/filtersets.py:1068 +msgid "Import target" +msgstr "" + +#: ipam/filtersets.py:53 ipam/filtersets.py:1074 +msgid "Import target (name)" +msgstr "" + +#: ipam/filtersets.py:58 ipam/filtersets.py:1079 +msgid "Export target" +msgstr "" + +#: ipam/filtersets.py:64 ipam/filtersets.py:1085 +msgid "Export target (name)" +msgstr "" + +#: ipam/filtersets.py:85 +msgid "Importing VRF" +msgstr "" + +#: ipam/filtersets.py:91 +msgid "Import VRF (RD)" +msgstr "" + +#: ipam/filtersets.py:96 +msgid "Exporting VRF" +msgstr "" + +#: ipam/filtersets.py:102 +msgid "Export VRF (RD)" +msgstr "" + +#: ipam/filtersets.py:132 ipam/filtersets.py:247 ipam/forms/model_forms.py:231 +#: ipam/tables/ip.py:211 templates/ipam/prefix.html:11 +msgid "Prefix" +msgstr "" + +#: ipam/filtersets.py:136 ipam/filtersets.py:175 ipam/filtersets.py:198 +msgid "RIR (ID)" +msgstr "" + +#: ipam/filtersets.py:142 ipam/filtersets.py:181 ipam/filtersets.py:204 +msgid "RIR (slug)" +msgstr "" + +#: ipam/filtersets.py:251 +msgid "Within prefix" +msgstr "" + +#: ipam/filtersets.py:255 +msgid "Within and including prefix" +msgstr "" + +#: ipam/filtersets.py:259 +msgid "Prefixes which contain this prefix or IP" +msgstr "" + +#: ipam/filtersets.py:338 ipam/filtersets.py:1191 +msgid "VLAN (ID)" +msgstr "" + +#: ipam/filtersets.py:342 ipam/filtersets.py:1186 +msgid "VLAN number (1-4094)" +msgstr "" + +#: ipam/filtersets.py:436 ipam/filtersets.py:440 ipam/filtersets.py:532 +#: ipam/forms/model_forms.py:446 templates/tenancy/contact.html:54 +#: tenancy/forms/bulk_edit.py:112 +msgid "Address" +msgstr "" + +#: ipam/filtersets.py:444 +msgid "Ranges which contain this prefix or IP" +msgstr "" + +#: ipam/filtersets.py:472 ipam/filtersets.py:528 +msgid "Parent prefix" +msgstr "" + +#: ipam/filtersets.py:536 ipam/forms/bulk_edit.py:328 +#: ipam/forms/filtersets.py:195 ipam/forms/filtersets.py:320 +msgid "Mask length" +msgstr "" + +#: ipam/filtersets.py:572 ipam/filtersets.py:807 ipam/filtersets.py:1026 +#: ipam/filtersets.py:1149 +msgid "Virtual machine (name)" +msgstr "" + +#: ipam/filtersets.py:577 ipam/filtersets.py:812 ipam/filtersets.py:1020 +#: ipam/filtersets.py:1154 virtualization/filtersets.py:273 +msgid "Virtual machine (ID)" +msgstr "" + +#: ipam/filtersets.py:583 ipam/filtersets.py:1160 +msgid "Interface (name)" +msgstr "" + +#: ipam/filtersets.py:588 ipam/filtersets.py:1165 +msgid "Interface (ID)" +msgstr "" + +#: ipam/filtersets.py:594 ipam/filtersets.py:1171 +msgid "VM interface (name)" +msgstr "" + +#: ipam/filtersets.py:599 +msgid "VM interface (ID)" +msgstr "" + +#: ipam/filtersets.py:604 +msgid "FHRP group (ID)" +msgstr "" + +#: ipam/filtersets.py:608 +msgid "Is assigned to an interface" +msgstr "" + +#: ipam/filtersets.py:612 +msgid "Is assigned" +msgstr "" + +#: ipam/filtersets.py:1031 +msgid "IP address (ID)" +msgstr "" + +#: ipam/filtersets.py:1037 ipam/models/ip.py:786 +msgid "IP address" +msgstr "" + +#: ipam/filtersets.py:1112 +msgid "L2VPN (slug)" +msgstr "" + +#: ipam/filtersets.py:1176 +msgid "VM Interface (ID)" +msgstr "" + +#: ipam/filtersets.py:1182 +msgid "VLAN (name)" +msgstr "" + +#: ipam/forms/bulk_create.py:14 +msgid "Address pattern" +msgstr "" + +#: ipam/forms/bulk_edit.py:87 +msgid "Is private" +msgstr "" + +#: ipam/forms/bulk_edit.py:108 ipam/forms/bulk_edit.py:137 +#: ipam/forms/bulk_edit.py:162 ipam/forms/bulk_import.py:91 +#: ipam/forms/bulk_import.py:111 ipam/forms/bulk_import.py:131 +#: ipam/forms/filtersets.py:113 ipam/forms/filtersets.py:128 +#: ipam/forms/filtersets.py:151 ipam/forms/model_forms.py:95 +#: ipam/forms/model_forms.py:110 ipam/forms/model_forms.py:132 +#: ipam/forms/model_forms.py:150 ipam/models/asns.py:31 ipam/models/asns.py:103 +#: ipam/models/ip.py:70 ipam/models/ip.py:89 ipam/tables/asn.py:20 +#: ipam/tables/asn.py:45 templates/ipam/aggregate.html:19 +#: templates/ipam/asn.html:28 templates/ipam/asnrange.html:20 +#: templates/ipam/rir.html:20 +msgid "RIR" +msgstr "" + +#: ipam/forms/bulk_edit.py:170 +msgid "Date added" +msgstr "" + +#: ipam/forms/bulk_edit.py:231 +msgid "Prefix length" +msgstr "" + +#: ipam/forms/bulk_edit.py:254 ipam/forms/filtersets.py:240 +#: templates/ipam/prefix.html:86 +msgid "Is a pool" +msgstr "" + +#: ipam/forms/bulk_edit.py:259 ipam/forms/bulk_edit.py:303 +#: ipam/models/ip.py:271 ipam/models/ip.py:538 +#, python-format +msgid "Treat as 100% utilized" +msgstr "" + +#: ipam/forms/bulk_edit.py:351 ipam/models/ip.py:771 +msgid "DNS name" +msgstr "" + +#: ipam/forms/bulk_edit.py:372 ipam/forms/bulk_edit.py:571 +#: ipam/forms/bulk_import.py:396 ipam/forms/bulk_import.py:480 +#: ipam/forms/bulk_import.py:506 ipam/forms/filtersets.py:379 +#: ipam/forms/filtersets.py:513 templates/ipam/fhrpgroup.html:23 +#: templates/ipam/inc/panels/fhrp_groups.html:11 templates/ipam/service.html:35 +#: templates/ipam/servicetemplate.html:20 +msgid "Protocol" +msgstr "" + +#: ipam/forms/bulk_edit.py:379 ipam/forms/filtersets.py:386 +#: ipam/tables/fhrp.py:22 templates/ipam/fhrpgroup.html:27 +msgid "Group ID" +msgstr "" + +#: ipam/forms/bulk_edit.py:384 ipam/forms/filtersets.py:391 +#: wireless/forms/bulk_edit.py:67 wireless/forms/bulk_edit.py:114 +#: wireless/forms/bulk_import.py:62 wireless/forms/bulk_import.py:65 +#: wireless/forms/bulk_import.py:104 wireless/forms/bulk_import.py:107 +#: wireless/forms/filtersets.py:53 wireless/forms/filtersets.py:87 +msgid "Authentication type" +msgstr "" + +#: ipam/forms/bulk_edit.py:389 ipam/forms/filtersets.py:395 +msgid "Authentication key" +msgstr "" + +#: ipam/forms/bulk_edit.py:406 ipam/forms/filtersets.py:372 +#: ipam/forms/model_forms.py:457 netbox/navigation/menu.py:356 +#: templates/ipam/fhrpgroup.html:51 +#: templates/wireless/inc/authentication_attrs.html:5 +#: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 +#: wireless/forms/filtersets.py:35 wireless/forms/filtersets.py:75 +#: wireless/forms/model_forms.py:56 wireless/forms/model_forms.py:161 +msgid "Authentication" +msgstr "" + +#: ipam/forms/bulk_edit.py:416 +msgid "Minimum child VLAN VID" +msgstr "" + +#: ipam/forms/bulk_edit.py:422 +msgid "Maximum child VLAN VID" +msgstr "" + +#: ipam/forms/bulk_edit.py:430 ipam/forms/model_forms.py:529 +msgid "Scope type" +msgstr "" + +#: ipam/forms/bulk_edit.py:491 ipam/forms/model_forms.py:602 +#: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 +msgid "Scope" +msgstr "" + +#: ipam/forms/bulk_edit.py:562 +msgid "Site & Group" +msgstr "" + +#: ipam/forms/bulk_edit.py:576 ipam/forms/model_forms.py:665 +#: ipam/forms/model_forms.py:699 ipam/tables/services.py:19 +#: ipam/tables/services.py:49 templates/ipam/service.html:39 +#: templates/ipam/servicetemplate.html:24 +msgid "Ports" +msgstr "" + +#: ipam/forms/bulk_import.py:50 +msgid "Import route targets" +msgstr "" + +#: ipam/forms/bulk_import.py:56 +msgid "Export route targets" +msgstr "" + +#: ipam/forms/bulk_import.py:94 ipam/forms/bulk_import.py:114 +#: ipam/forms/bulk_import.py:134 +msgid "Assigned RIR" +msgstr "" + +#: ipam/forms/bulk_import.py:184 +msgid "VLAN's group (if any)" +msgstr "" + +#: ipam/forms/bulk_import.py:187 ipam/forms/bulk_import.py:564 +#: ipam/forms/filtersets.py:603 ipam/forms/model_forms.py:221 +#: ipam/forms/model_forms.py:804 ipam/models/vlans.py:213 ipam/tables/ip.py:254 +#: templates/ipam/l2vpntermination_edit.html:17 templates/ipam/prefix.html:61 +#: templates/ipam/vlan.html:12 templates/ipam/vlan/base.html:6 +#: templates/ipam/vlan_edit.html:10 templates/wireless/wirelesslan.html:31 +#: wireless/forms/bulk_edit.py:54 wireless/forms/bulk_import.py:48 +#: wireless/forms/model_forms.py:49 wireless/models.py:101 +msgid "VLAN" +msgstr "" + +#: ipam/forms/bulk_import.py:310 +msgid "Parent device of assigned interface (if any)" +msgstr "" + +#: ipam/forms/bulk_import.py:313 ipam/forms/bulk_import.py:499 +#: ipam/forms/bulk_import.py:550 ipam/forms/model_forms.py:693 +#: virtualization/filtersets.py:279 virtualization/forms/bulk_edit.py:197 +#: virtualization/forms/bulk_import.py:145 +#: virtualization/forms/filtersets.py:200 +#: virtualization/forms/model_forms.py:280 +msgid "Virtual machine" +msgstr "" + +#: ipam/forms/bulk_import.py:317 +msgid "Parent VM of assigned interface (if any)" +msgstr "" + +#: ipam/forms/bulk_import.py:324 +msgid "Assigned interface" +msgstr "" + +#: ipam/forms/bulk_import.py:327 +msgid "Is primary" +msgstr "" + +#: ipam/forms/bulk_import.py:328 +msgid "Make this the primary IP for the assigned device" +msgstr "" + +#: ipam/forms/bulk_import.py:367 +msgid "No device or virtual machine specified; cannot set as primary IP" +msgstr "" + +#: ipam/forms/bulk_import.py:371 +msgid "No interface specified; cannot set as primary IP" +msgstr "" + +#: ipam/forms/bulk_import.py:400 +msgid "Auth type" +msgstr "" + +#: ipam/forms/bulk_import.py:415 +msgid "Scope type (app & model)" +msgstr "" + +#: ipam/forms/bulk_import.py:421 +#, python-brace-format +msgid "Minimum child VLAN VID (default: {minimum})" +msgstr "" + +#: ipam/forms/bulk_import.py:427 +#, python-brace-format +msgid "Maximum child VLAN VID (default: {maximum})" +msgstr "" + +#: ipam/forms/bulk_import.py:451 +msgid "Assigned VLAN group" +msgstr "" + +#: ipam/forms/bulk_import.py:482 ipam/forms/bulk_import.py:508 +msgid "IP protocol" +msgstr "" + +#: ipam/forms/bulk_import.py:496 +msgid "Required if not assigned to a VM" +msgstr "" + +#: ipam/forms/bulk_import.py:503 +msgid "Required if not assigned to a device" +msgstr "" + +#: ipam/forms/bulk_import.py:526 +msgid "L2VPN type" +msgstr "" + +#: ipam/forms/bulk_import.py:547 +msgid "Parent device (for interface)" +msgstr "" + +#: ipam/forms/bulk_import.py:554 +msgid "Parent virtual machine (for interface)" +msgstr "" + +#: ipam/forms/bulk_import.py:561 +msgid "Assigned interface (device or VM)" +msgstr "" + +#: ipam/forms/bulk_import.py:594 +msgid "Cannot import device and VM interface terminations simultaneously." +msgstr "" + +#: ipam/forms/bulk_import.py:596 +msgid "Each termination must specify either an interface or a VLAN." +msgstr "" + +#: ipam/forms/bulk_import.py:598 +msgid "Cannot assign both an interface and a VLAN." +msgstr "" + +#: ipam/forms/filtersets.py:50 ipam/forms/model_forms.py:62 +#: ipam/forms/model_forms.py:780 netbox/navigation/menu.py:177 +msgid "Route Targets" +msgstr "" + +#: ipam/forms/filtersets.py:56 ipam/forms/filtersets.py:544 +#: ipam/forms/model_forms.py:49 ipam/forms/model_forms.py:767 +msgid "Import targets" +msgstr "" + +#: ipam/forms/filtersets.py:61 ipam/forms/filtersets.py:549 +#: ipam/forms/model_forms.py:54 ipam/forms/model_forms.py:772 +msgid "Export targets" +msgstr "" + +#: ipam/forms/filtersets.py:76 +msgid "Imported by VRF" +msgstr "" + +#: ipam/forms/filtersets.py:81 +msgid "Exported by VRF" +msgstr "" + +#: ipam/forms/filtersets.py:90 ipam/tables/ip.py:89 templates/ipam/rir.html:33 +msgid "Private" +msgstr "" + +#: ipam/forms/filtersets.py:108 ipam/forms/filtersets.py:190 +#: ipam/forms/filtersets.py:265 ipam/forms/filtersets.py:315 +msgid "Address family" +msgstr "" + +#: ipam/forms/filtersets.py:122 templates/ipam/asnrange.html:26 +msgid "Range" +msgstr "" + +#: ipam/forms/filtersets.py:131 +msgid "Start" +msgstr "" + +#: ipam/forms/filtersets.py:135 +msgid "End" +msgstr "" + +#: ipam/forms/filtersets.py:185 +msgid "Search within" +msgstr "" + +#: ipam/forms/filtersets.py:206 ipam/forms/filtersets.py:331 +msgid "Present in VRF" +msgstr "" + +#: ipam/forms/filtersets.py:247 ipam/forms/filtersets.py:286 +#, python-format +msgid "Marked as 100% utilized" +msgstr "" + +#: ipam/forms/filtersets.py:301 +msgid "Device/VM" +msgstr "" + +#: ipam/forms/filtersets.py:336 +msgid "Assigned Device" +msgstr "" + +#: ipam/forms/filtersets.py:341 +msgid "Assigned VM" +msgstr "" + +#: ipam/forms/filtersets.py:355 +msgid "Assigned to an interface" +msgstr "" + +#: ipam/forms/filtersets.py:362 templates/ipam/ipaddress.html:54 +msgid "DNS Name" +msgstr "" + +#: ipam/forms/filtersets.py:404 ipam/forms/filtersets.py:496 +#: ipam/models/vlans.py:154 templates/ipam/vlan.html:34 +msgid "VLAN ID" +msgstr "" + +#: ipam/forms/filtersets.py:436 +msgid "Minimum VID" +msgstr "" + +#: ipam/forms/filtersets.py:442 +msgid "Maximum VID" +msgstr "" + +#: ipam/forms/filtersets.py:518 +msgid "Port" +msgstr "" + +#: ipam/forms/filtersets.py:558 ipam/tables/ip.py:424 +#: templates/ipam/l2vpntermination.html:19 +msgid "Assigned Object" +msgstr "" + +#: ipam/forms/filtersets.py:570 +msgid "Assigned Object Type" +msgstr "" + +#: ipam/forms/filtersets.py:612 ipam/tables/vlans.py:191 +#: templates/ipam/ipaddress_edit.html:47 +#: templates/ipam/l2vpntermination_edit.html:27 +#: templates/ipam/service_create.html:22 templates/ipam/service_edit.html:21 +#: templates/virtualization/virtualmachine.html:13 +#: templates/virtualization/vminterface.html:24 +#: virtualization/forms/filtersets.py:186 +#: virtualization/forms/model_forms.py:221 +#: virtualization/tables/virtualmachines.py:110 +msgid "Virtual Machine" +msgstr "" + +#: ipam/forms/model_forms.py:115 ipam/tables/ip.py:116 +#: templates/ipam/aggregate.html:11 templates/ipam/prefix.html:38 +msgid "Aggregate" +msgstr "" + +#: ipam/forms/model_forms.py:136 templates/ipam/asnrange.html:12 +msgid "ASN Range" +msgstr "" + +#: ipam/forms/model_forms.py:232 +msgid "Site/VLAN Assignment" +msgstr "" + +#: ipam/forms/model_forms.py:258 templates/ipam/iprange.html:11 +msgid "IP Range" +msgstr "" + +#: ipam/forms/model_forms.py:287 ipam/forms/model_forms.py:456 +#: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 +msgid "FHRP Group" +msgstr "" + +#: ipam/forms/model_forms.py:302 +msgid "Make this the primary IP for the device/VM" +msgstr "" + +#: ipam/forms/model_forms.py:353 +msgid "An IP address can only be assigned to a single object." +msgstr "" + +#: ipam/forms/model_forms.py:359 ipam/models/ip.py:877 +msgid "" +"Cannot reassign IP address while it is designated as the primary IP for the " +"parent object" +msgstr "" + +#: ipam/forms/model_forms.py:369 +msgid "" +"Only IP addresses assigned to an interface can be designated as primary IPs." +msgstr "" + +#: ipam/forms/model_forms.py:375 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "" + +#: ipam/forms/model_forms.py:381 +#, python-brace-format +msgid "{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "" + +#: ipam/forms/model_forms.py:458 +msgid "Virtual IP Address" +msgstr "" + +#: ipam/forms/model_forms.py:600 ipam/forms/model_forms.py:639 +#: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 +#: templates/ipam/vlangroup.html:27 +msgid "VLAN Group" +msgstr "" + +#: ipam/forms/model_forms.py:601 +msgid "Child VLANs" +msgstr "" + +#: ipam/forms/model_forms.py:670 ipam/forms/model_forms.py:704 +msgid "" +"Comma-separated list of one or more port numbers. A range may be specified " +"using a hyphen." +msgstr "" + +#: ipam/forms/model_forms.py:675 templates/ipam/servicetemplate.html:12 +msgid "Service Template" +msgstr "" + +#: ipam/forms/model_forms.py:726 +msgid "Service template" +msgstr "" + +#: ipam/forms/model_forms.py:846 +msgid "A termination must specify an interface or VLAN." +msgstr "" + +#: ipam/forms/model_forms.py:848 +msgid "" +"A termination can only have one terminating object (an interface or VLAN)." +msgstr "" + +#: ipam/models/asns.py:34 +msgid "start" +msgstr "" + +#: ipam/models/asns.py:51 +msgid "ASN range" +msgstr "" + +#: ipam/models/asns.py:52 +msgid "ASN ranges" +msgstr "" + +#: ipam/models/asns.py:72 +#, python-brace-format +msgid "Starting ASN ({start}) must be lower than ending ASN ({end})." +msgstr "" + +#: ipam/models/asns.py:104 +msgid "Regional Internet Registry responsible for this AS number space" +msgstr "" + +#: ipam/models/asns.py:109 +msgid "16- or 32-bit autonomous system number" +msgstr "" + +#: ipam/models/fhrp.py:23 +msgid "group ID" +msgstr "" + +#: ipam/models/fhrp.py:31 ipam/models/services.py:22 +msgid "protocol" +msgstr "" + +#: ipam/models/fhrp.py:39 wireless/models.py:27 +msgid "authentication type" +msgstr "" + +#: ipam/models/fhrp.py:44 +msgid "authentication key" +msgstr "" + +#: ipam/models/fhrp.py:57 +msgid "FHRP group" +msgstr "" + +#: ipam/models/fhrp.py:58 +msgid "FHRP groups" +msgstr "" + +#: ipam/models/fhrp.py:94 tenancy/models/contacts.py:133 +msgid "priority" +msgstr "" + +#: ipam/models/fhrp.py:111 +msgid "FHRP group assignment" +msgstr "" + +#: ipam/models/fhrp.py:112 +msgid "FHRP group assignments" +msgstr "" + +#: ipam/models/ip.py:64 +msgid "private" +msgstr "" + +#: ipam/models/ip.py:65 +msgid "IP space managed by this RIR is considered private" +msgstr "" + +#: ipam/models/ip.py:71 netbox/navigation/menu.py:170 +msgid "RIRs" +msgstr "" + +#: ipam/models/ip.py:83 +msgid "IPv4 or IPv6 network" +msgstr "" + +#: ipam/models/ip.py:90 +msgid "Regional Internet Registry responsible for this IP space" +msgstr "" + +#: ipam/models/ip.py:100 +msgid "date added" +msgstr "" + +#: ipam/models/ip.py:114 +msgid "aggregate" +msgstr "" + +#: ipam/models/ip.py:115 +msgid "aggregates" +msgstr "" + +#: ipam/models/ip.py:131 +msgid "Cannot create aggregate with /0 mask." +msgstr "" + +#: ipam/models/ip.py:143 +#, python-brace-format +msgid "" +"Aggregates cannot overlap. {prefix} is already covered by an existing " +"aggregate ({aggregate})." +msgstr "" + +#: ipam/models/ip.py:157 +#, python-brace-format +msgid "" +"Prefixes cannot overlap aggregates. {prefix} covers an existing aggregate " +"({aggregate})." +msgstr "" + +#: ipam/models/ip.py:199 ipam/models/ip.py:736 +msgid "role" +msgstr "" + +#: ipam/models/ip.py:200 +msgid "roles" +msgstr "" + +#: ipam/models/ip.py:216 ipam/models/ip.py:292 +msgid "prefix" +msgstr "" + +#: ipam/models/ip.py:217 +msgid "IPv4 or IPv6 network with mask" +msgstr "" + +#: ipam/models/ip.py:253 +msgid "Operational status of this prefix" +msgstr "" + +#: ipam/models/ip.py:261 +msgid "The primary function of this prefix" +msgstr "" + +#: ipam/models/ip.py:264 +msgid "is a pool" +msgstr "" + +#: ipam/models/ip.py:266 +msgid "All IP addresses within this prefix are considered usable" +msgstr "" + +#: ipam/models/ip.py:269 ipam/models/ip.py:536 +msgid "mark utilized" +msgstr "" + +#: ipam/models/ip.py:293 +msgid "prefixes" +msgstr "" + +#: ipam/models/ip.py:316 +msgid "Cannot create prefix with /0 mask." +msgstr "" + +#: ipam/models/ip.py:323 ipam/models/ip.py:853 +#, python-brace-format +msgid "VRF {vrf}" +msgstr "" + +#: ipam/models/ip.py:323 ipam/models/ip.py:853 +msgid "global table" +msgstr "" + +#: ipam/models/ip.py:325 +#, python-brace-format +msgid "Duplicate prefix found in {table}: {prefix}" +msgstr "" + +#: ipam/models/ip.py:494 +msgid "start address" +msgstr "" + +#: ipam/models/ip.py:495 ipam/models/ip.py:499 ipam/models/ip.py:711 +msgid "IPv4 or IPv6 address (with mask)" +msgstr "" + +#: ipam/models/ip.py:498 +msgid "end address" +msgstr "" + +#: ipam/models/ip.py:525 +msgid "Operational status of this range" +msgstr "" + +#: ipam/models/ip.py:533 +msgid "The primary function of this range" +msgstr "" + +#: ipam/models/ip.py:547 +msgid "IP range" +msgstr "" + +#: ipam/models/ip.py:548 +msgid "IP ranges" +msgstr "" + +#: ipam/models/ip.py:564 +msgid "Starting and ending IP address versions must match" +msgstr "" + +#: ipam/models/ip.py:570 +msgid "Starting and ending IP address masks must match" +msgstr "" + +#: ipam/models/ip.py:577 +#, python-brace-format +msgid "" +"Ending address must be lower than the starting address ({start_address})" +msgstr "" + +#: ipam/models/ip.py:589 +#, python-brace-format +msgid "Defined addresses overlap with range {overlapping_range} in VRF {vrf}" +msgstr "" + +#: ipam/models/ip.py:598 +#, python-brace-format +msgid "Defined range exceeds maximum supported size ({max_size})" +msgstr "" + +#: ipam/models/ip.py:710 tenancy/models/contacts.py:81 +msgid "address" +msgstr "" + +#: ipam/models/ip.py:733 +msgid "The operational status of this IP" +msgstr "" + +#: ipam/models/ip.py:740 +msgid "The functional role of this IP" +msgstr "" + +#: ipam/models/ip.py:764 templates/ipam/ipaddress.html:75 +msgid "NAT (inside)" +msgstr "" + +#: ipam/models/ip.py:765 +msgid "The IP for which this address is the \"outside\" IP" +msgstr "" + +#: ipam/models/ip.py:772 +msgid "Hostname or FQDN (not case-sensitive)" +msgstr "" + +#: ipam/models/ip.py:787 ipam/models/services.py:94 +msgid "IP addresses" +msgstr "" + +#: ipam/models/ip.py:843 +msgid "Cannot create IP address with /0 mask." +msgstr "" + +#: ipam/models/ip.py:855 +#, python-brace-format +msgid "Duplicate IP address found in {table}: {ipaddress}" +msgstr "" + +#: ipam/models/ip.py:884 +msgid "Only IPv6 addresses can be assigned SLAAC status" +msgstr "" + +#: ipam/models/l2vpn.py:64 netbox/navigation/menu.py:205 +msgid "L2VPNs" +msgstr "" + +#: ipam/models/l2vpn.py:113 +msgid "L2VPN termination" +msgstr "" + +#: ipam/models/l2vpn.py:114 +msgid "L2VPN terminations" +msgstr "" + +#: ipam/models/l2vpn.py:132 +#, python-brace-format +msgid "L2VPN Termination already assigned ({assigned_object})" +msgstr "" + +#: ipam/models/l2vpn.py:144 +#, python-brace-format +msgid "" +"{l2vpn_type} L2VPNs cannot have more than two terminations; found " +"{terminations_count} already defined." +msgstr "" + +#: ipam/models/services.py:33 +msgid "port numbers" +msgstr "" + +#: ipam/models/services.py:59 +msgid "service template" +msgstr "" + +#: ipam/models/services.py:60 +msgid "service templates" +msgstr "" + +#: ipam/models/services.py:95 +msgid "The specific IP addresses (if any) to which this service is bound" +msgstr "" + +#: ipam/models/services.py:102 +msgid "service" +msgstr "" + +#: ipam/models/services.py:103 +msgid "services" +msgstr "" + +#: ipam/models/services.py:117 +msgid "" +"A service cannot be associated with both a device and a virtual machine." +msgstr "" + +#: ipam/models/services.py:119 +msgid "A service must be associated with either a device or a virtual machine." +msgstr "" + +#: ipam/models/vlans.py:50 +msgid "minimum VLAN ID" +msgstr "" + +#: ipam/models/vlans.py:56 +msgid "Lowest permissible ID of a child VLAN" +msgstr "" + +#: ipam/models/vlans.py:59 +msgid "maximum VLAN ID" +msgstr "" + +#: ipam/models/vlans.py:65 +msgid "Highest permissible ID of a child VLAN" +msgstr "" + +#: ipam/models/vlans.py:83 +msgid "VLAN groups" +msgstr "" + +#: ipam/models/vlans.py:93 +msgid "Cannot set scope_type without scope_id." +msgstr "" + +#: ipam/models/vlans.py:95 +msgid "Cannot set scope_id without scope_type." +msgstr "" + +#: ipam/models/vlans.py:100 +msgid "Maximum child VID must be greater than or equal to minimum child VID" +msgstr "" + +#: ipam/models/vlans.py:143 +msgid "The specific site to which this VLAN is assigned (if any)" +msgstr "" + +#: ipam/models/vlans.py:151 +msgid "VLAN group (optional)" +msgstr "" + +#: ipam/models/vlans.py:159 +msgid "Numeric VLAN ID (1-4094)" +msgstr "" + +#: ipam/models/vlans.py:177 +msgid "Operational status of this VLAN" +msgstr "" + +#: ipam/models/vlans.py:185 +msgid "The primary function of this VLAN" +msgstr "" + +#: ipam/models/vlans.py:214 ipam/tables/ip.py:175 ipam/tables/vlans.py:78 +#: ipam/views.py:942 netbox/navigation/menu.py:181 +#: netbox/navigation/menu.py:183 +msgid "VLANs" +msgstr "" + +#: ipam/models/vlans.py:229 +#, python-brace-format +msgid "" +"VLAN is assigned to group {group} (scope: {scope}); cannot also assign to " +"site {site}." +msgstr "" + +#: ipam/models/vlans.py:237 +#, python-brace-format +msgid "VID must be between {minimum} and {maximum} for VLANs in group {group}" +msgstr "" + +#: ipam/models/vrfs.py:30 +msgid "route distinguisher" +msgstr "" + +#: ipam/models/vrfs.py:31 +msgid "Unique route distinguisher (as defined in RFC 4364)" +msgstr "" + +#: ipam/models/vrfs.py:42 +msgid "enforce unique space" +msgstr "" + +#: ipam/models/vrfs.py:43 +msgid "Prevent duplicate prefixes/IP addresses within this VRF" +msgstr "" + +#: ipam/models/vrfs.py:63 netbox/navigation/menu.py:174 +#: netbox/navigation/menu.py:176 +msgid "VRFs" +msgstr "" + +#: ipam/models/vrfs.py:82 +msgid "Route target value (formatted in accordance with RFC 4360)" +msgstr "" + +#: ipam/models/vrfs.py:94 +msgid "route target" +msgstr "" + +#: ipam/models/vrfs.py:95 +msgid "route targets" +msgstr "" + +#: ipam/tables/asn.py:51 +msgid "ASDOT" +msgstr "" + +#: ipam/tables/asn.py:56 +msgid "Site Count" +msgstr "" + +#: ipam/tables/asn.py:61 +msgid "Provider Count" +msgstr "" + +#: ipam/tables/ip.py:94 netbox/navigation/menu.py:167 +#: netbox/navigation/menu.py:169 +msgid "Aggregates" +msgstr "" + +#: ipam/tables/ip.py:124 +msgid "Added" +msgstr "" + +#: ipam/tables/ip.py:127 ipam/tables/ip.py:165 ipam/tables/vlans.py:138 +#: ipam/views.py:351 netbox/navigation/menu.py:153 +#: netbox/navigation/menu.py:155 templates/ipam/vlan.html:87 +msgid "Prefixes" +msgstr "" + +#: ipam/tables/ip.py:130 ipam/tables/ip.py:267 ipam/tables/ip.py:320 +#: ipam/tables/vlans.py:82 templates/dcim/device.html:280 +#: templates/ipam/aggregate.html:25 templates/ipam/iprange.html:32 +#: templates/ipam/prefix.html:100 +msgid "Utilization" +msgstr "" + +#: ipam/tables/ip.py:170 netbox/navigation/menu.py:149 +msgid "IP Ranges" +msgstr "" + +#: ipam/tables/ip.py:220 +msgid "Prefix (Flat)" +msgstr "" + +#: ipam/tables/ip.py:224 templates/dcim/rack_edit.html:52 +msgid "Depth" +msgstr "" + +#: ipam/tables/ip.py:233 +msgid "Children" +msgstr "" + +#: ipam/tables/ip.py:261 +msgid "Pool" +msgstr "" + +#: ipam/tables/ip.py:264 ipam/tables/ip.py:317 +msgid "Marked Utilized" +msgstr "" + +#: ipam/tables/ip.py:301 +msgid "Start address" +msgstr "" + +#: ipam/tables/ip.py:379 +msgid "NAT (Inside)" +msgstr "" + +#: ipam/tables/ip.py:384 +msgid "NAT (Outside)" +msgstr "" + +#: ipam/tables/ip.py:389 +msgid "Assigned" +msgstr "" + +#: ipam/tables/l2vpn.py:27 ipam/tables/vrfs.py:36 +msgid "Import Targets" +msgstr "" + +#: ipam/tables/l2vpn.py:32 ipam/tables/vrfs.py:41 +msgid "Export Targets" +msgstr "" + +#: ipam/tables/l2vpn.py:69 +msgid "Object Parent" +msgstr "" + +#: ipam/tables/l2vpn.py:74 +msgid "Object Site" +msgstr "" + +#: ipam/tables/vlans.py:68 +msgid "Scope Type" +msgstr "" + +#: ipam/tables/vlans.py:107 ipam/tables/vlans.py:210 +#: templates/dcim/inc/interface_vlans_table.html:4 +msgid "VID" +msgstr "" + +#: ipam/tables/vrfs.py:30 +msgid "RD" +msgstr "" + +#: ipam/tables/vrfs.py:33 +msgid "Unique" +msgstr "" + +#: ipam/views.py:538 +msgid "Child Prefixes" +msgstr "" + +#: ipam/views.py:573 +msgid "Child Ranges" +msgstr "" + +#: ipam/views.py:870 +msgid "Related IPs" +msgstr "" + +#: ipam/views.py:1093 +msgid "Device Interfaces" +msgstr "" + +#: ipam/views.py:1111 +msgid "VM Interfaces" +msgstr "" + +#: netbox/config/parameters.py:22 templates/extras/configrevision.html:111 +msgid "Login banner" +msgstr "" + +#: netbox/config/parameters.py:24 +msgid "Additional content to display on the login page" +msgstr "" + +#: netbox/config/parameters.py:33 templates/extras/configrevision.html:115 +msgid "Maintenance banner" +msgstr "" + +#: netbox/config/parameters.py:35 +msgid "Additional content to display when in maintenance mode" +msgstr "" + +#: netbox/config/parameters.py:44 templates/extras/configrevision.html:119 +msgid "Top banner" +msgstr "" + +#: netbox/config/parameters.py:46 +msgid "Additional content to display at the top of every page" +msgstr "" + +#: netbox/config/parameters.py:55 templates/extras/configrevision.html:123 +msgid "Bottom banner" +msgstr "" + +#: netbox/config/parameters.py:57 +msgid "Additional content to display at the bottom of every page" +msgstr "" + +#: netbox/config/parameters.py:68 +msgid "Globally unique IP space" +msgstr "" + +#: netbox/config/parameters.py:70 +msgid "Enforce unique IP addressing within the global table" +msgstr "" + +#: netbox/config/parameters.py:75 templates/extras/configrevision.html:87 +msgid "Prefer IPv4" +msgstr "" + +#: netbox/config/parameters.py:77 +msgid "Prefer IPv4 addresses over IPv6" +msgstr "" + +#: netbox/config/parameters.py:84 +msgid "Rack unit height" +msgstr "" + +#: netbox/config/parameters.py:86 +msgid "Default unit height for rendered rack elevations" +msgstr "" + +#: netbox/config/parameters.py:91 +msgid "Rack unit width" +msgstr "" + +#: netbox/config/parameters.py:93 +msgid "Default unit width for rendered rack elevations" +msgstr "" + +#: netbox/config/parameters.py:100 +msgid "Powerfeed voltage" +msgstr "" + +#: netbox/config/parameters.py:102 +msgid "Default voltage for powerfeeds" +msgstr "" + +#: netbox/config/parameters.py:107 +msgid "Powerfeed amperage" +msgstr "" + +#: netbox/config/parameters.py:109 +msgid "Default amperage for powerfeeds" +msgstr "" + +#: netbox/config/parameters.py:114 +msgid "Powerfeed max utilization" +msgstr "" + +#: netbox/config/parameters.py:116 +msgid "Default max utilization for powerfeeds" +msgstr "" + +#: netbox/config/parameters.py:123 templates/extras/configrevision.html:99 +msgid "Allowed URL schemes" +msgstr "" + +#: netbox/config/parameters.py:128 +msgid "Permitted schemes for URLs in user-provided content" +msgstr "" + +#: netbox/config/parameters.py:136 +msgid "Default page size" +msgstr "" + +#: netbox/config/parameters.py:142 +msgid "Maximum page size" +msgstr "" + +#: netbox/config/parameters.py:150 templates/extras/configrevision.html:151 +msgid "Custom validators" +msgstr "" + +#: netbox/config/parameters.py:152 +msgid "Custom validation rules (JSON)" +msgstr "" + +#: netbox/config/parameters.py:164 +msgid "Default preferences" +msgstr "" + +#: netbox/config/parameters.py:166 +msgid "Default preferences for new users" +msgstr "" + +#: netbox/config/parameters.py:173 templates/extras/configrevision.html:175 +msgid "Maintenance mode" +msgstr "" + +#: netbox/config/parameters.py:175 +msgid "Enable maintenance mode" +msgstr "" + +#: netbox/config/parameters.py:180 templates/extras/configrevision.html:179 +msgid "GraphQL enabled" +msgstr "" + +#: netbox/config/parameters.py:182 +msgid "Enable the GraphQL API" +msgstr "" + +#: netbox/config/parameters.py:187 templates/extras/configrevision.html:183 +msgid "Changelog retention" +msgstr "" + +#: netbox/config/parameters.py:189 +msgid "Days to retain changelog history (set to zero for unlimited)" +msgstr "" + +#: netbox/config/parameters.py:194 +msgid "Job result retention" +msgstr "" + +#: netbox/config/parameters.py:196 +msgid "Days to retain job result history (set to zero for unlimited)" +msgstr "" + +#: netbox/config/parameters.py:201 templates/extras/configrevision.html:191 +msgid "Maps URL" +msgstr "" + +#: netbox/config/parameters.py:203 +msgid "Base URL for mapping geographic locations" +msgstr "" + +#: netbox/forms/__init__.py:13 +msgid "Partial match" +msgstr "" + +#: netbox/forms/__init__.py:14 +msgid "Exact match" +msgstr "" + +#: netbox/forms/__init__.py:15 +msgid "Starts with" +msgstr "" + +#: netbox/forms/__init__.py:16 +msgid "Ends with" +msgstr "" + +#: netbox/forms/__init__.py:17 +msgid "Regex" +msgstr "" + +#: netbox/forms/__init__.py:35 +msgid "Object type(s)" +msgstr "" + +#: netbox/forms/base.py:66 +msgid "Id" +msgstr "" + +#: netbox/forms/base.py:107 +msgid "Add tags" +msgstr "" + +#: netbox/forms/base.py:112 +msgid "Remove tags" +msgstr "" + +#: netbox/models/features.py:422 +msgid "Remote data source" +msgstr "" + +#: netbox/models/features.py:432 +msgid "data path" +msgstr "" + +#: netbox/models/features.py:436 +msgid "Path to remote file (relative to data source root)" +msgstr "" + +#: netbox/models/features.py:439 +msgid "auto sync enabled" +msgstr "" + +#: netbox/models/features.py:441 +msgid "Enable automatic synchronization of data when the data file is updated" +msgstr "" + +#: netbox/models/features.py:444 +msgid "date synced" +msgstr "" + +#: netbox/navigation/menu.py:12 +msgid "Organization" +msgstr "" + +#: netbox/navigation/menu.py:20 +msgid "Site Groups" +msgstr "" + +#: netbox/navigation/menu.py:28 +msgid "Rack Roles" +msgstr "" + +#: netbox/navigation/menu.py:32 +msgid "Elevations" +msgstr "" + +#: netbox/navigation/menu.py:41 +msgid "Tenant Groups" +msgstr "" + +#: netbox/navigation/menu.py:48 +msgid "Contact Groups" +msgstr "" + +#: netbox/navigation/menu.py:49 templates/tenancy/contactrole.html:8 +msgid "Contact Roles" +msgstr "" + +#: netbox/navigation/menu.py:50 +msgid "Contact Assignments" +msgstr "" + +#: netbox/navigation/menu.py:64 +msgid "Modules" +msgstr "" + +#: netbox/navigation/menu.py:65 templates/dcim/devicerole.html:8 +msgid "Device Roles" +msgstr "" + +#: netbox/navigation/menu.py:68 templates/dcim/device.html:179 +#: templates/dcim/virtualdevicecontext.html:8 +msgid "Virtual Device Contexts" +msgstr "" + +#: netbox/navigation/menu.py:76 +msgid "Manufacturers" +msgstr "" + +#: netbox/navigation/menu.py:80 +msgid "Device Components" +msgstr "" + +#: netbox/navigation/menu.py:92 templates/dcim/inventoryitemrole.html:8 +msgid "Inventory Item Roles" +msgstr "" + +#: netbox/navigation/menu.py:99 netbox/navigation/menu.py:103 +msgid "Connections" +msgstr "" + +#: netbox/navigation/menu.py:105 +msgid "Cables" +msgstr "" + +#: netbox/navigation/menu.py:106 +msgid "Wireless Links" +msgstr "" + +#: netbox/navigation/menu.py:109 +msgid "Interface Connections" +msgstr "" + +#: netbox/navigation/menu.py:114 +msgid "Console Connections" +msgstr "" + +#: netbox/navigation/menu.py:119 +msgid "Power Connections" +msgstr "" + +#: netbox/navigation/menu.py:135 +msgid "Wireless LAN Groups" +msgstr "" + +#: netbox/navigation/menu.py:156 +msgid "Prefix & VLAN Roles" +msgstr "" + +#: netbox/navigation/menu.py:162 +msgid "ASN Ranges" +msgstr "" + +#: netbox/navigation/menu.py:184 +msgid "VLAN Groups" +msgstr "" + +#: netbox/navigation/menu.py:191 +msgid "Service Templates" +msgstr "" + +#: netbox/navigation/menu.py:192 templates/dcim/device.html:321 +#: templates/ipam/ipaddress.html:122 +#: templates/virtualization/virtualmachine.html:155 +msgid "Services" +msgstr "" + +#: netbox/navigation/menu.py:199 +msgid "Overlay" +msgstr "" + +#: netbox/navigation/menu.py:206 templates/ipam/l2vpn.html:57 +msgid "Terminations" +msgstr "" + +#: netbox/navigation/menu.py:213 templates/dcim/device_edit.html:78 +msgid "Virtualization" +msgstr "" + +#: netbox/navigation/menu.py:217 netbox/navigation/menu.py:219 +#: virtualization/views.py:186 +msgid "Virtual Machines" +msgstr "" + +#: netbox/navigation/menu.py:227 +msgid "Cluster Types" +msgstr "" + +#: netbox/navigation/menu.py:228 +msgid "Cluster Groups" +msgstr "" + +#: netbox/navigation/menu.py:242 +msgid "Circuit Types" +msgstr "" + +#: netbox/navigation/menu.py:246 netbox/navigation/menu.py:248 +msgid "Providers" +msgstr "" + +#: netbox/navigation/menu.py:249 templates/circuits/provider.html:53 +msgid "Provider Accounts" +msgstr "" + +#: netbox/navigation/menu.py:250 +msgid "Provider Networks" +msgstr "" + +#: netbox/navigation/menu.py:264 +msgid "Power Panels" +msgstr "" + +#: netbox/navigation/menu.py:275 +msgid "Configurations" +msgstr "" + +#: netbox/navigation/menu.py:277 +msgid "Config Contexts" +msgstr "" + +#: netbox/navigation/menu.py:278 +msgid "Config Templates" +msgstr "" + +#: netbox/navigation/menu.py:285 netbox/navigation/menu.py:289 +msgid "Customization" +msgstr "" + +#: netbox/navigation/menu.py:291 +#: templates/circuits/circuittermination_edit.html:53 +#: templates/dcim/cable_edit.html:77 templates/dcim/device_edit.html:103 +#: templates/dcim/inventoryitem_edit.html:102 templates/dcim/rack_edit.html:81 +#: templates/dcim/virtualchassis_add.html:31 +#: templates/dcim/virtualchassis_edit.html:41 +#: templates/generic/bulk_edit.html:92 templates/htmx/form.html:32 +#: templates/inc/panels/custom_fields.html:7 +#: templates/ipam/ipaddress_bulk_add.html:35 +#: templates/ipam/ipaddress_edit.html:88 +#: templates/ipam/l2vpntermination_edit.html:51 +#: templates/ipam/service_create.html:75 templates/ipam/service_edit.html:62 +#: templates/ipam/vlan_edit.html:63 +msgid "Custom Fields" +msgstr "" + +#: netbox/navigation/menu.py:292 +msgid "Custom Field Choices" +msgstr "" + +#: netbox/navigation/menu.py:293 +msgid "Custom Links" +msgstr "" + +#: netbox/navigation/menu.py:294 +msgid "Export Templates" +msgstr "" + +#: netbox/navigation/menu.py:295 +msgid "Saved Filters" +msgstr "" + +#: netbox/navigation/menu.py:297 +msgid "Image Attachments" +msgstr "" + +#: netbox/navigation/menu.py:301 +msgid "Reports & Scripts" +msgstr "" + +#: netbox/navigation/menu.py:321 +msgid "Operations" +msgstr "" + +#: netbox/navigation/menu.py:325 +msgid "Integrations" +msgstr "" + +#: netbox/navigation/menu.py:327 +msgid "Data Sources" +msgstr "" + +#: netbox/navigation/menu.py:328 +msgid "Webhooks" +msgstr "" + +#: netbox/navigation/menu.py:332 netbox/navigation/menu.py:336 +#: netbox/views/generic/feature_views.py:151 +#: templates/extras/report/base.html:37 templates/extras/script/base.html:36 +msgid "Jobs" +msgstr "" + +#: netbox/navigation/menu.py:342 +msgid "Logging" +msgstr "" + +#: netbox/navigation/menu.py:344 +msgid "Journal Entries" +msgstr "" + +#: netbox/navigation/menu.py:345 templates/extras/objectchange.html:8 +#: templates/extras/objectchange_list.html:4 +msgid "Change Log" +msgstr "" + +#: netbox/navigation/menu.py:352 templates/inc/profile_button.html:18 +msgid "Admin" +msgstr "" + +#: netbox/navigation/menu.py:361 templates/users/group.html:27 +#: users/forms/model_forms.py:242 users/forms/model_forms.py:255 +#: users/forms/model_forms.py:309 users/tables.py:105 +msgid "Users" +msgstr "" + +#: netbox/navigation/menu.py:384 users/forms/model_forms.py:182 +#: users/forms/model_forms.py:195 users/forms/model_forms.py:314 +#: users/tables.py:35 users/tables.py:109 +msgid "Groups" +msgstr "" + +#: netbox/navigation/menu.py:406 templates/account/base.html:21 +#: templates/inc/profile_button.html:39 +msgid "API Tokens" +msgstr "" + +#: netbox/navigation/menu.py:413 users/forms/model_forms.py:188 +#: users/forms/model_forms.py:197 users/forms/model_forms.py:248 +#: users/forms/model_forms.py:256 +msgid "Permissions" +msgstr "" + +#: netbox/navigation/menu.py:425 +msgid "Current Config" +msgstr "" + +#: netbox/navigation/menu.py:431 +msgid "Config Revisions" +msgstr "" + +#: netbox/navigation/menu.py:471 templates/500.html:35 +#: templates/account/preferences.html:29 +msgid "Plugins" +msgstr "" + +#: netbox/preferences.py:17 +msgid "Color mode" +msgstr "" + +#: netbox/preferences.py:25 +msgid "Page length" +msgstr "" + +#: netbox/preferences.py:27 +msgid "The default number of objects to display per page" +msgstr "" + +#: netbox/preferences.py:31 +msgid "Paginator placement" +msgstr "" + +#: netbox/preferences.py:37 +msgid "Where the paginator controls will be displayed relative to a table" +msgstr "" + +#: netbox/preferences.py:43 +msgid "Data format" +msgstr "" + +#: netbox/tables/columns.py:175 +msgid "Toggle all" +msgstr "" + +#: netbox/tables/columns.py:277 templates/inc/profile_button.html:56 +msgid "Toggle Dropdown" +msgstr "" + +#: netbox/tables/columns.py:542 +msgid "Error" +msgstr "" + +#: netbox/tables/tables.py:234 templates/generic/bulk_import.html:115 +msgid "Field" +msgstr "" + +#: netbox/tables/tables.py:237 +msgid "Value" +msgstr "" + +#: netbox/tables/tables.py:246 +msgid "No results found" +msgstr "" + +#: netbox/tests/dummy_plugin/navigation.py:29 +msgid "Dummy Plugin" +msgstr "" + +#: netbox/views/generic/feature_views.py:38 +msgid "Changelog" +msgstr "" + +#: netbox/views/generic/feature_views.py:91 +msgid "Journal" +msgstr "" + +#: templates/403.html:4 +msgid "Access Denied" +msgstr "" + +#: templates/403.html:9 +msgid "You do not have permission to access this page" +msgstr "" + +#: templates/404.html:4 +msgid "Page Not Found" +msgstr "" + +#: templates/404.html:9 +msgid "The requested page does not exist" +msgstr "" + +#: templates/500.html:7 templates/500.html:18 +msgid "Server Error" +msgstr "" + +#: templates/500.html:23 +msgid "There was a problem with your request. Please contact an administrator" +msgstr "" + +#: templates/500.html:28 +msgid "The complete exception is provided below" +msgstr "" + +#: templates/500.html:33 +msgid "Python version" +msgstr "" + +#: templates/500.html:34 +msgid "NetBox version" +msgstr "" + +#: templates/500.html:36 +msgid "None installed" +msgstr "" + +#: templates/500.html:39 +msgid "If further assistance is required, please post to the" +msgstr "" + +#: templates/500.html:39 +msgid "NetBox discussion forum" +msgstr "" + +#: templates/500.html:39 +msgid "on GitHub" +msgstr "" + +#: templates/500.html:42 templates/base/40x.html:17 +msgid "Home Page" +msgstr "" + +#: templates/account/base.html:7 templates/inc/profile_button.html:24 +msgid "Profile" +msgstr "" + +#: templates/account/base.html:13 templates/inc/profile_button.html:34 +msgid "Preferences" +msgstr "" + +#: templates/account/password.html:5 +msgid "Change Password" +msgstr "" + +#: templates/account/password.html:17 templates/account/preferences.html:82 +#: templates/dcim/devicebay_populate.html:34 +#: templates/dcim/virtualchassis_add_member.html:24 +#: templates/dcim/virtualchassis_edit.html:104 +#: templates/extras/configrevision_restore.html:80 +#: templates/extras/object_journal.html:26 templates/extras/script.html:36 +#: templates/generic/bulk_add_component.html:55 +#: templates/generic/bulk_delete.html:46 templates/generic/bulk_edit.html:125 +#: templates/generic/bulk_import.html:53 templates/generic/bulk_import.html:75 +#: templates/generic/bulk_import.html:97 templates/generic/bulk_remove.html:42 +#: templates/generic/bulk_rename.html:44 +#: templates/generic/confirmation_form.html:20 +#: templates/generic/object_edit.html:76 templates/htmx/delete_form.html:19 +#: templates/htmx/delete_form.html:21 templates/ipam/ipaddress_assign.html:31 +#: templates/virtualization/cluster_add_devices.html:30 +msgid "Cancel" +msgstr "" + +#: templates/account/password.html:18 templates/account/preferences.html:83 +#: templates/dcim/devicebay_populate.html:35 +#: templates/dcim/virtualchassis_add_member.html:26 +#: templates/dcim/virtualchassis_edit.html:106 +#: 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:66 +#: utilities/templates/helpers/applied_filters.html:16 +#: utilities/templates/helpers/table_config_form.html:40 +msgid "Save" +msgstr "" + +#: templates/account/preferences.html:41 +msgid "Table Configurations" +msgstr "" + +#: templates/account/preferences.html:46 +msgid "Clear table preferences" +msgstr "" + +#: templates/account/preferences.html:53 +msgid "Toggle All" +msgstr "" + +#: templates/account/preferences.html:55 +msgid "Table" +msgstr "" + +#: templates/account/preferences.html:56 +msgid "Ordering" +msgstr "" + +#: templates/account/preferences.html:57 +msgid "Columns" +msgstr "" + +#: templates/account/preferences.html:76 templates/dcim/cable_trace.html:113 +#: templates/extras/object_configcontext.html:55 +msgid "None found" +msgstr "" + +#: templates/account/profile.html:6 +msgid "User Profile" +msgstr "" + +#: templates/account/profile.html:12 +msgid "Account Details" +msgstr "" + +#: templates/account/profile.html:30 templates/tenancy/contact.html:44 +#: templates/users/user.html:26 tenancy/forms/bulk_edit.py:108 +msgid "Email" +msgstr "" + +#: templates/account/profile.html:34 templates/users/user.html:30 +msgid "Account Created" +msgstr "" + +#: templates/account/profile.html:38 templates/users/user.html:42 +msgid "Superuser" +msgstr "" + +#: templates/account/profile.html:42 +msgid "Admin Access" +msgstr "" + +#: templates/account/profile.html:51 templates/users/objectpermission.html:86 +#: templates/users/user.html:51 +msgid "Assigned Groups" +msgstr "" + +#: templates/account/profile.html:56 +#: templates/circuits/circuit_terminations_swap.html:18 +#: templates/circuits/circuit_terminations_swap.html:26 +#: templates/circuits/inc/circuit_termination.html:154 +#: templates/dcim/devicebay.html:66 +#: templates/dcim/inc/panels/inventory_items.html:37 +#: templates/dcim/interface.html:302 templates/dcim/modulebay.html:79 +#: templates/extras/configcontext.html:73 +#: templates/extras/htmx/script_result.html:54 +#: templates/extras/object_configcontext.html:28 +#: templates/extras/objectchange.html:128 +#: templates/extras/objectchange.html:145 templates/extras/webhook.html:122 +#: templates/extras/webhook.html:134 templates/extras/webhook.html:146 +#: templates/inc/panel_table.html:12 templates/inc/panels/comments.html:12 +#: templates/ipam/inc/panels/fhrp_groups.html:43 templates/users/group.html:32 +#: templates/users/group.html:42 templates/users/objectpermission.html:81 +#: templates/users/objectpermission.html:91 templates/users/user.html:56 +#: templates/users/user.html:66 +msgid "None" +msgstr "" + +#: templates/account/profile.html:66 templates/users/user.html:76 +msgid "Recent Activity" +msgstr "" + +#: templates/account/token.html:8 templates/account/token_list.html:6 +msgid "My API Tokens" +msgstr "" + +#: templates/account/token.html:11 templates/account/token.html:19 +#: templates/users/token.html:6 templates/users/token.html:14 +#: users/forms/filtersets.py:123 +msgid "Token" +msgstr "" + +#: templates/account/token.html:40 templates/users/token.html:32 +#: users/forms/bulk_edit.py:87 +msgid "Write enabled" +msgstr "" + +#: templates/account/token.html:52 templates/users/token.html:44 +msgid "Last used" +msgstr "" + +#: templates/account/token_list.html:12 +msgid "Add a Token" +msgstr "" + +#: templates/admin/index.html:10 +msgid "System" +msgstr "" + +#: templates/admin/index.html:14 +msgid "Background Tasks" +msgstr "" + +#: templates/admin/index.html:19 +msgid "Installed plugins" +msgstr "" + +#: templates/base/base.html:28 templates/extras/admin/plugins_list.html:8 +#: templates/home.html:24 +msgid "Home" +msgstr "" + +#: templates/base/layout.html:27 templates/base/layout.html:37 +#: templates/login.html:34 +msgid "NetBox logo" +msgstr "" + +#: templates/base/layout.html:76 +msgid "Debug mode is enabled" +msgstr "" + +#: templates/base/layout.html:77 +msgid "" +"Performance may be limited. Debugging should never be enabled on a " +"production system" +msgstr "" + +#: templates/base/layout.html:83 +msgid "Maintenance Mode" +msgstr "" + +#: templates/base/layout.html:134 +msgid "Docs" +msgstr "" + +#: templates/base/layout.html:139 templates/rest_framework/api.html:10 +msgid "REST API" +msgstr "" + +#: templates/base/layout.html:144 +msgid "REST API documentation" +msgstr "" + +#: templates/base/layout.html:150 +msgid "GraphQL API" +msgstr "" + +#: templates/base/layout.html:156 +msgid "Source Code" +msgstr "" + +#: templates/base/layout.html:161 +msgid "Community" +msgstr "" + +#: templates/base/sidenav.html:12 templates/base/sidenav.html:17 +msgid "NetBox Logo" +msgstr "" + +#: templates/circuits/circuit.html:48 +msgid "Install Date" +msgstr "" + +#: templates/circuits/circuit.html:52 +msgid "Termination Date" +msgstr "" + +#: templates/circuits/circuit_terminations_swap.html:4 +msgid "Swap Circuit Terminations" +msgstr "" + +#: templates/circuits/circuit_terminations_swap.html:8 +#, python-format +msgid "Swap these terminations for circuit %(circuit)s?" +msgstr "" + +#: templates/circuits/circuit_terminations_swap.html:14 +msgid "A side" +msgstr "" + +#: templates/circuits/circuit_terminations_swap.html:22 +msgid "Z side" +msgstr "" + +#: templates/circuits/circuittermination_edit.html:9 +#: templates/circuits/inc/circuit_termination.html:81 +#: templates/dcim/frontport.html:128 templates/dcim/interface.html:195 +#: templates/dcim/rearport.html:118 +msgid "Circuit Termination" +msgstr "" + +#: templates/circuits/circuittermination_edit.html:41 +msgid "Termination Details" +msgstr "" + +#: templates/circuits/circuittype.html:10 +msgid "Add Circuit" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:9 +#: templates/dcim/devicetype/component_templates.html:30 +#: templates/dcim/manufacturer.html:11 +#: templates/dcim/moduletype/component_templates.html:30 +#: templates/generic/bulk_add_component.html:8 +#: templates/users/objectpermission.html:41 +#: utilities/templates/buttons/add.html:4 +#: utilities/templates/helpers/table_config_form.html:20 +msgid "Add" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:14 +#: templates/circuits/inc/circuit_termination.html:63 +#: templates/dcim/devicetype/component_templates.html:21 +#: templates/dcim/inc/panels/inventory_items.html:24 +#: templates/dcim/moduletype/component_templates.html:21 +#: templates/dcim/powerpanel.html:61 templates/generic/object_edit.html:29 +#: templates/ipam/inc/ipaddress_edit_header.html:10 +#: templates/ipam/inc/panels/fhrp_groups.html:30 +#: utilities/templates/buttons/edit.html:3 +msgid "Edit" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:17 +msgid "Swap" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:26 +#, python-format +msgid "Termination %(side)s" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:42 +#: templates/dcim/cable.html:70 templates/dcim/cable.html:76 +msgid "Termination" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:46 +#: templates/dcim/consoleport.html:62 templates/dcim/consoleserverport.html:62 +#: templates/dcim/powerfeed.html:122 +msgid "Marked as connected" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:48 +msgid "to" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:58 +#: templates/circuits/inc/circuit_termination.html:59 +#: templates/dcim/frontport.html:87 +#: templates/dcim/inc/connection_endpoints.html:7 +#: templates/dcim/interface.html:156 templates/dcim/rearport.html:83 +msgid "Trace" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:62 +msgid "Edit cable" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:67 +msgid "Remove cable" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:68 +#: 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:66 +msgid "Disconnect" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:75 +#: templates/dcim/consoleport.html:71 templates/dcim/consoleserverport.html:71 +#: templates/dcim/frontport.html:109 templates/dcim/interface.html:182 +#: templates/dcim/interface.html:202 templates/dcim/powerfeed.html:136 +#: templates/dcim/poweroutlet.html:75 templates/dcim/poweroutlet.html:76 +#: templates/dcim/powerport.html:77 templates/dcim/rearport.html:105 +msgid "Connect" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:79 +#: templates/dcim/consoleport.html:78 templates/dcim/consoleserverport.html:78 +#: templates/dcim/frontport.html:18 templates/dcim/frontport.html:122 +#: templates/dcim/interface.html:189 templates/dcim/inventoryitem_edit.html:49 +#: templates/dcim/rearport.html:112 +msgid "Front Port" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:97 +msgid "Downstream" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:98 +msgid "Upstream" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:107 +msgid "Cross-Connect" +msgstr "" + +#: templates/circuits/inc/circuit_termination.html:111 +msgid "Patch Panel/Port" +msgstr "" + +#: templates/circuits/provider.html:11 +msgid "Add circuit" +msgstr "" + +#: templates/circuits/provideraccount.html:17 +msgid "Provider Account" +msgstr "" + +#: templates/core/datafile.html:47 +msgid "Last Updated" +msgstr "" + +#: templates/core/datafile.html:51 templates/ipam/iprange.html:28 +msgid "Size" +msgstr "" + +#: templates/core/datafile.html:52 +msgid "bytes" +msgstr "" + +#: templates/core/datafile.html:55 +msgid "SHA256 Hash" +msgstr "" + +#: templates/core/datasource.html:14 templates/core/datasource.html:20 +#: utilities/templates/buttons/sync.html:5 +msgid "Sync" +msgstr "" + +#: templates/core/datasource.html:51 +msgid "Last synced" +msgstr "" + +#: templates/core/datasource.html:86 +msgid "Backend" +msgstr "" + +#: templates/core/datasource.html:102 +msgid "No parameters defined" +msgstr "" + +#: templates/core/datasource.html:118 +msgid "Files" +msgstr "" + +#: templates/core/job.html:21 +msgid "Job" +msgstr "" + +#: templates/core/job.html:39 templates/extras/journalentry.html:29 +msgid "Created By" +msgstr "" + +#: templates/core/job.html:48 +msgid "Scheduling" +msgstr "" + +#: templates/core/job.html:60 +#, python-format +msgid "every %(interval)s seconds" +msgstr "" + +#: templates/dcim/bulk_disconnect.html:9 +#, python-format +msgid "" +"Are you sure you want to disconnect these %(count)s %(obj_type_plural)s?" +msgstr "" + +#: templates/dcim/cable_edit.html:12 +msgid "A Side" +msgstr "" + +#: templates/dcim/cable_edit.html:29 +msgid "B Side" +msgstr "" + +#: templates/dcim/cable_trace.html:6 +#, python-format +msgid "Cable Trace for %(object_type)s %(object)s" +msgstr "" + +#: templates/dcim/cable_trace.html:21 templates/dcim/inc/rack_elevation.html:7 +msgid "Download SVG" +msgstr "" + +#: templates/dcim/cable_trace.html:27 +msgid "Asymmetric Path" +msgstr "" + +#: templates/dcim/cable_trace.html:28 +msgid "The nodes below have no links and result in an asymmetric path" +msgstr "" + +#: templates/dcim/cable_trace.html:35 +msgid "Path split" +msgstr "" + +#: templates/dcim/cable_trace.html:36 +msgid "Select a node below to continue" +msgstr "" + +#: templates/dcim/cable_trace.html:52 +msgid "Trace Completed" +msgstr "" + +#: templates/dcim/cable_trace.html:55 +msgid "Total segments" +msgstr "" + +#: templates/dcim/cable_trace.html:59 +msgid "Total length" +msgstr "" + +#: templates/dcim/cable_trace.html:74 +msgid "No paths found" +msgstr "" + +#: templates/dcim/cable_trace.html:83 +msgid "Related Paths" +msgstr "" + +#: templates/dcim/cable_trace.html:89 +msgid "Origin" +msgstr "" + +#: templates/dcim/cable_trace.html:90 +msgid "Destination" +msgstr "" + +#: templates/dcim/cable_trace.html:91 +msgid "Segments" +msgstr "" + +#: templates/dcim/cable_trace.html:104 +msgid "Incomplete" +msgstr "" + +#: templates/dcim/component_list.html:14 +msgid "Rename Selected" +msgstr "" + +#: templates/dcim/consoleport.html:67 templates/dcim/consoleserverport.html:67 +#: templates/dcim/frontport.html:105 templates/dcim/interface.html:178 +#: templates/dcim/poweroutlet.html:73 templates/dcim/powerport.html:73 +msgid "Not Connected" +msgstr "" + +#: templates/dcim/consoleport.html:75 templates/dcim/consoleserverport.html:18 +#: templates/dcim/frontport.html:116 templates/dcim/inventoryitem_edit.html:44 +msgid "Console Server Port" +msgstr "" + +#: templates/dcim/device.html:52 +msgid "Highlight device" +msgstr "" + +#: templates/dcim/device.html:74 +msgid "Not racked" +msgstr "" + +#: templates/dcim/device.html:81 templates/dcim/site.html:109 +msgid "GPS Coordinates" +msgstr "" + +#: templates/dcim/device.html:87 templates/dcim/site.html:115 +msgid "Map It" +msgstr "" + +#: templates/dcim/device.html:127 templates/dcim/inventoryitem.html:57 +#: templates/dcim/module.html:79 templates/dcim/modulebay.html:73 +#: templates/dcim/rack.html:69 +msgid "Asset Tag" +msgstr "" + +#: templates/dcim/device.html:170 +msgid "View Virtual Chassis" +msgstr "" + +#: templates/dcim/device.html:187 +msgid "Create VDC" +msgstr "" + +#: templates/dcim/device.html:196 templates/dcim/device_edit.html:64 +#: virtualization/forms/model_forms.py:224 +msgid "Management" +msgstr "" + +#: templates/dcim/device.html:217 templates/dcim/device.html:233 +#: templates/virtualization/virtualmachine.html:56 +#: templates/virtualization/virtualmachine.html:72 +msgid "NAT for" +msgstr "" + +#: templates/dcim/device.html:219 templates/dcim/device.html:235 +#: templates/virtualization/virtualmachine.html:58 +#: templates/virtualization/virtualmachine.html:74 +msgid "NAT" +msgstr "" + +#: templates/dcim/device.html:271 templates/dcim/rack.html:77 +msgid "Power Utilization" +msgstr "" + +#: templates/dcim/device.html:276 +msgid "Input" +msgstr "" + +#: templates/dcim/device.html:277 +msgid "Outlets" +msgstr "" + +#: templates/dcim/device.html:278 +msgid "Allocated" +msgstr "" + +#: templates/dcim/device.html:287 templates/dcim/device.html:289 +#: templates/dcim/device.html:305 templates/dcim/powerfeed.html:70 +msgid "VA" +msgstr "" + +#: templates/dcim/device.html:299 +msgctxt "Leg of a power feed" +msgid "Leg" +msgstr "" + +#: templates/dcim/device.html:329 +#: templates/virtualization/virtualmachine.html:163 +msgid "Add a service" +msgstr "" + +#: templates/dcim/device.html:336 templates/dcim/rack.html:84 +#: templates/dcim/rack_edit.html:38 +msgid "Dimensions" +msgstr "" + +#: templates/dcim/device/base.html:21 templates/dcim/device_list.html:9 +#: templates/dcim/devicetype/base.html:18 templates/dcim/module.html:18 +#: templates/dcim/moduletype/base.html:18 +#: templates/virtualization/virtualmachine_list.html:8 +msgid "Add Components" +msgstr "" + +#: templates/dcim/device/consoleports.html:24 +msgid "Add Console Ports" +msgstr "" + +#: templates/dcim/device/consoleserverports.html:24 +msgid "Add Console Server Ports" +msgstr "" + +#: templates/dcim/device/devicebays.html:10 +msgid "Add Device Bays" +msgstr "" + +#: templates/dcim/device/frontports.html:24 +msgid "Add Front Ports" +msgstr "" + +#: templates/dcim/device/inc/interface_table_controls.html:9 +msgid "Hide Enabled" +msgstr "" + +#: templates/dcim/device/inc/interface_table_controls.html:10 +msgid "Hide Disabled" +msgstr "" + +#: templates/dcim/device/inc/interface_table_controls.html:11 +msgid "Hide Virtual" +msgstr "" + +#: templates/dcim/device/inc/interface_table_controls.html:12 +msgid "Hide Disconnected" +msgstr "" + +#: templates/dcim/device/interfaces.html:28 +#: templates/virtualization/virtualmachine/base.html:21 +msgid "Add Interfaces" +msgstr "" + +#: templates/dcim/device/inventory.html:10 +#: templates/dcim/inc/panels/inventory_items.html:46 +msgid "Add Inventory Item" +msgstr "" + +#: templates/dcim/device/modulebays.html:10 +msgid "Add Module Bays" +msgstr "" + +#: templates/dcim/device/poweroutlets.html:24 +msgid "Add Power Outlets" +msgstr "" + +#: templates/dcim/device/powerports.html:24 +msgid "Add Power Port" +msgstr "" + +#: templates/dcim/device/rearports.html:24 +msgid "Add Rear Ports" +msgstr "" + +#: templates/dcim/device/render_config.html:5 +#: templates/virtualization/virtualmachine/render_config.html:5 +msgid "Config" +msgstr "" + +#: templates/dcim/device/render_config.html:37 +#: templates/virtualization/virtualmachine/render_config.html:37 +msgid "Context Data" +msgstr "" + +#: templates/dcim/device/render_config.html:57 +#: templates/virtualization/virtualmachine/render_config.html:57 +msgid "Download" +msgstr "" + +#: templates/dcim/device/render_config.html:60 +#: templates/virtualization/virtualmachine/render_config.html:60 +msgid "Rendered Config" +msgstr "" + +#: templates/dcim/device/render_config.html:65 +#: templates/virtualization/virtualmachine/render_config.html:65 +msgid "No configuration template found" +msgstr "" + +#: templates/dcim/device_edit.html:44 +msgid "Parent Bay" +msgstr "" + +#: templates/dcim/device_edit.html:48 +#: utilities/templates/form_helpers/render_field.html:20 +msgid "Regenerate Slug" +msgstr "" + +#: templates/dcim/device_edit.html:49 templates/generic/bulk_remove.html:7 +#: utilities/templates/helpers/table_config_form.html:23 +msgid "Remove" +msgstr "" + +#: templates/dcim/device_edit.html:110 +msgid "Local Config Context Data" +msgstr "" + +#: templates/dcim/device_list.html:82 +#: templates/dcim/devicetype/component_templates.html:18 +#: templates/dcim/moduletype/component_templates.html:18 +#: templates/generic/bulk_rename.html:34 +#: templates/virtualization/virtualmachine/interfaces.html:11 +msgid "Rename" +msgstr "" + +#: templates/dcim/devicebay.html:18 +msgid "Device Bay" +msgstr "" + +#: templates/dcim/devicebay.html:48 +msgid "Installed Device" +msgstr "" + +#: templates/dcim/devicebay_delete.html:6 +#, python-format +msgid "Delete device bay %(devicebay)s?" +msgstr "" + +#: templates/dcim/devicebay_delete.html:11 +#, python-format +msgid "" +"Are you sure you want to delete this device bay from %(device)s?" +msgstr "" + +#: templates/dcim/devicebay_depopulate.html:6 +#, python-format +msgid "Remove %(device)s from %(device_bay)s?" +msgstr "" + +#: templates/dcim/devicebay_depopulate.html:13 +#, python-format +msgid "" +"Are you sure you want to remove %(device)s from " +"%(device_bay)s?" +msgstr "" + +#: templates/dcim/devicebay_populate.html:13 +msgid "Populate" +msgstr "" + +#: templates/dcim/devicebay_populate.html:22 +msgid "Bay" +msgstr "" + +#: templates/dcim/devicerole.html:14 templates/dcim/platform.html:17 +msgid "Add Device" +msgstr "" + +#: templates/dcim/devicerole.html:43 +msgid "VM Role" +msgstr "" + +#: templates/dcim/devicetype.html:21 templates/dcim/moduletype.html:19 +msgid "Model Name" +msgstr "" + +#: templates/dcim/devicetype.html:28 templates/dcim/moduletype.html:23 +msgid "Part Number" +msgstr "" + +#: templates/dcim/devicetype.html:40 +msgid "Height (U" +msgstr "" + +#: templates/dcim/devicetype.html:44 +msgid "Exclude From Utilization" +msgstr "" + +#: templates/dcim/devicetype.html:62 +msgid "Parent/Child" +msgstr "" + +#: templates/dcim/devicetype.html:74 +msgid "Front Image" +msgstr "" + +#: templates/dcim/devicetype.html:86 +msgid "Rear Image" +msgstr "" + +#: templates/dcim/frontport.html:57 +msgid "Rear Port Position" +msgstr "" + +#: templates/dcim/frontport.html:79 templates/dcim/interface.html:146 +#: templates/dcim/poweroutlet.html:67 templates/dcim/powerport.html:67 +#: templates/dcim/rearport.html:75 +msgid "Marked as Connected" +msgstr "" + +#: templates/dcim/frontport.html:93 templates/dcim/rearport.html:89 +msgid "Connection Status" +msgstr "" + +#: templates/dcim/inc/cable_termination.html:65 +msgid "No termination" +msgstr "" + +#: templates/dcim/inc/cable_toggle_buttons.html:4 +msgid "Mark Planned" +msgstr "" + +#: templates/dcim/inc/cable_toggle_buttons.html:8 +msgid "Mark Installed" +msgstr "" + +#: templates/dcim/inc/connection_endpoints.html:13 +msgid "Path Status" +msgstr "" + +#: templates/dcim/inc/connection_endpoints.html:18 +msgid "Not Reachable" +msgstr "" + +#: templates/dcim/inc/connection_endpoints.html:23 +msgid "Path Endpoints" +msgstr "" + +#: templates/dcim/inc/endpoint_connection.html:8 +#: templates/dcim/powerfeed.html:128 templates/dcim/rearport.html:101 +msgid "Not connected" +msgstr "" + +#: templates/dcim/inc/interface_vlans_table.html:6 +msgid "Untagged" +msgstr "" + +#: templates/dcim/inc/interface_vlans_table.html:37 +msgid "No VLANs Assigned" +msgstr "" + +#: templates/dcim/inc/interface_vlans_table.html:44 +#: templates/ipam/prefix_list.html:16 templates/ipam/prefix_list.html:33 +msgid "Clear" +msgstr "" + +#: templates/dcim/inc/interface_vlans_table.html:47 +msgid "Clear All" +msgstr "" + +#: templates/dcim/interface.html:17 +msgid "Add Child Interface" +msgstr "" + +#: templates/dcim/interface.html:51 +msgid "Speed/Duplex" +msgstr "" + +#: templates/dcim/interface.html:74 +msgid "PoE Mode" +msgstr "" + +#: templates/dcim/interface.html:78 +msgid "PoE Type" +msgstr "" + +#: templates/dcim/interface.html:82 +#: templates/virtualization/vminterface.html:66 +msgid "802.1Q Mode" +msgstr "" + +#: templates/dcim/interface.html:126 +#: templates/virtualization/vminterface.html:62 +msgid "MAC Address" +msgstr "" + +#: templates/dcim/interface.html:153 +msgid "Wireless Link" +msgstr "" + +#: templates/dcim/interface.html:222 +msgid "Peer" +msgstr "" + +#: templates/dcim/interface.html:234 +#: templates/wireless/inc/wirelesslink_interface.html:26 +msgid "Channel" +msgstr "" + +#: templates/dcim/interface.html:243 +#: templates/wireless/inc/wirelesslink_interface.html:32 +msgid "Channel Frequency" +msgstr "" + +#: templates/dcim/interface.html:246 templates/dcim/interface.html:254 +#: templates/dcim/interface.html:265 templates/dcim/interface.html:273 +msgid "MHz" +msgstr "" + +#: templates/dcim/interface.html:262 +#: templates/wireless/inc/wirelesslink_interface.html:42 +msgid "Channel Width" +msgstr "" + +#: templates/dcim/interface.html:291 templates/wireless/wirelesslan.html:15 +#: templates/wireless/wirelesslink.html:24 wireless/forms/bulk_edit.py:59 +#: wireless/forms/bulk_edit.py:101 wireless/forms/filtersets.py:39 +#: wireless/forms/filtersets.py:79 wireless/models.py:81 wireless/models.py:155 +#: wireless/tables/wirelesslan.py:44 +msgid "SSID" +msgstr "" + +#: templates/dcim/interface.html:312 +msgid "LAG Members" +msgstr "" + +#: templates/dcim/interface.html:331 +msgid "No member interfaces" +msgstr "" + +#: templates/dcim/interface.html:355 templates/ipam/fhrpgroup.html:80 +#: templates/ipam/iprange/ip_addresses.html:7 +#: templates/ipam/prefix/ip_addresses.html:7 +#: templates/virtualization/vminterface.html:92 +msgid "Add IP Address" +msgstr "" + +#: templates/dcim/inventoryitem.html:25 +msgid "Parent Item" +msgstr "" + +#: templates/dcim/inventoryitem.html:49 +msgid "Part ID" +msgstr "" + +#: templates/dcim/inventoryitem_bulk_delete.html:5 +msgid "This will also delete all child inventory items of those listed" +msgstr "" + +#: templates/dcim/inventoryitem_edit.html:33 +msgid "Component Assignment" +msgstr "" + +#: templates/dcim/inventoryitem_edit.html:59 templates/dcim/poweroutlet.html:18 +#: templates/dcim/powerport.html:81 +msgid "Power Outlet" +msgstr "" + +#: templates/dcim/location.html:17 +msgid "Add Child Location" +msgstr "" + +#: templates/dcim/location.html:76 +msgid "Child Locations" +msgstr "" + +#: templates/dcim/location.html:84 templates/dcim/site.html:150 +msgid "Add a Location" +msgstr "" + +#: templates/dcim/location.html:98 templates/dcim/site.html:164 +msgid "Add a Device" +msgstr "" + +#: templates/dcim/manufacturer.html:16 +msgid "Add Device Type" +msgstr "" + +#: templates/dcim/manufacturer.html:21 +msgid "Add Module Type" +msgstr "" + +#: templates/dcim/powerfeed.html:56 +msgid "Connected Device" +msgstr "" + +#: templates/dcim/powerfeed.html:66 +msgid "Utilization (Allocated" +msgstr "" + +#: templates/dcim/powerfeed.html:85 +msgid "Electrical Characteristics" +msgstr "" + +#: templates/dcim/powerfeed.html:95 +msgctxt "Abbreviation for volts" +msgid "V" +msgstr "" + +#: templates/dcim/powerfeed.html:99 +msgctxt "Abbreviation for amperes" +msgid "A" +msgstr "" + +#: templates/dcim/poweroutlet.html:51 +msgid "Feed Leg" +msgstr "" + +#: templates/dcim/powerpanel.html:77 +msgid "Add Power Feeds" +msgstr "" + +#: templates/dcim/powerport.html:47 +msgid "Maximum Draw" +msgstr "" + +#: templates/dcim/powerport.html:51 +msgid "Allocated Draw" +msgstr "" + +#: templates/dcim/rack.html:73 +msgid "Space Utilization" +msgstr "" + +#: templates/dcim/rack.html:103 +msgid "descending" +msgstr "" + +#: templates/dcim/rack.html:103 +msgid "ascending" +msgstr "" + +#: templates/dcim/rack.html:106 +msgid "Starting Unit" +msgstr "" + +#: templates/dcim/rack.html:132 +msgid "Mounting Depth" +msgstr "" + +#: templates/dcim/rack.html:142 +msgid "Rack Weight" +msgstr "" + +#: templates/dcim/rack.html:152 templates/dcim/rack_edit.html:67 +msgid "Maximum Weight" +msgstr "" + +#: templates/dcim/rack.html:162 +msgid "Total Weight" +msgstr "" + +#: templates/dcim/rack.html:180 templates/dcim/rack_elevation_list.html:16 +msgid "Images and Labels" +msgstr "" + +#: templates/dcim/rack.html:181 templates/dcim/rack_elevation_list.html:17 +msgid "Images only" +msgstr "" + +#: templates/dcim/rack.html:182 templates/dcim/rack_elevation_list.html:18 +msgid "Labels only" +msgstr "" + +#: templates/dcim/rack/reservations.html:9 +msgid "Add reservation" +msgstr "" + +#: templates/dcim/rack_edit.html:21 +msgid "Inventory Control" +msgstr "" + +#: templates/dcim/rack_edit.html:45 +msgid "Outer Dimensions" +msgstr "" + +#: templates/dcim/rack_edit.html:56 templates/dcim/rack_edit.html:71 +msgid "Unit" +msgstr "" + +#: templates/dcim/rack_elevation_list.html:12 +msgid "View List" +msgstr "" + +#: templates/dcim/rack_elevation_list.html:27 +msgid "Sort By" +msgstr "" + +#: templates/dcim/rack_elevation_list.html:77 +msgid "No Racks Found" +msgstr "" + +#: templates/dcim/rack_list.html:8 +msgid "View Elevations" +msgstr "" + +#: templates/dcim/rackreservation.html:47 +msgid "Reservation Details" +msgstr "" + +#: templates/dcim/rackrole.html:10 +msgid "Add Rack" +msgstr "" + +#: templates/dcim/rearport.html:53 +msgid "Positions" +msgstr "" + +#: templates/dcim/region.html:17 templates/dcim/sitegroup.html:17 +msgid "Add Site" +msgstr "" + +#: templates/dcim/region.html:56 +msgid "Child Regions" +msgstr "" + +#: templates/dcim/region.html:64 +msgid "Add Region" +msgstr "" + +#: templates/dcim/site.html:69 +msgid "Facility" +msgstr "" + +#: templates/dcim/site.html:77 +msgid "Time Zone" +msgstr "" + +#: templates/dcim/site.html:80 +msgid "UTC" +msgstr "" + +#: templates/dcim/site.html:81 +msgid "Site time" +msgstr "" + +#: templates/dcim/site.html:88 +msgid "Physical Address" +msgstr "" + +#: templates/dcim/site.html:94 +msgid "Map" +msgstr "" + +#: templates/dcim/site.html:105 +msgid "Shipping Address" +msgstr "" + +#: templates/dcim/sitegroup.html:56 templates/tenancy/contactgroup.html:49 +#: templates/tenancy/tenantgroup.html:58 +#: templates/wireless/wirelesslangroup.html:56 +msgid "Child Groups" +msgstr "" + +#: templates/dcim/sitegroup.html:64 +msgid "Add Site Group" +msgstr "" + +#: templates/dcim/trace/attachment.html:5 +#: templates/extras/exporttemplate.html:37 +msgid "Attachment" +msgstr "" + +#: templates/dcim/virtualchassis.html:86 +msgid "Add Member" +msgstr "" + +#: templates/dcim/virtualchassis_add.html:18 +msgid "Member Devices" +msgstr "" + +#: templates/dcim/virtualchassis_add_member.html:6 +#, python-format +msgid "Add New Member to Virtual Chassis %(virtual_chassis)s" +msgstr "" + +#: templates/dcim/virtualchassis_add_member.html:17 +msgid "Add New Member" +msgstr "" + +#: templates/dcim/virtualchassis_add_member.html:25 +msgid "Add Another" +msgstr "" + +#: templates/dcim/virtualchassis_edit.html:7 +#, python-format +msgid "Editing Virtual Chassis %(name)s" +msgstr "" + +#: templates/dcim/virtualchassis_edit.html:54 +msgid "Rack/Unit" +msgstr "" + +#: templates/dcim/virtualchassis_remove_member.html:5 +msgid "Remove Virtual Chassis Member" +msgstr "" + +#: 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 "" + +#: templates/dcim/virtualdevicecontext.html:29 templates/ipam/l2vpn.html:19 +msgid "Identifier" +msgstr "" + +#: templates/exceptions/import_error.html:6 +msgid "" +"A module import error occurred during this request. Common causes include " +"the following:" +msgstr "" + +#: templates/exceptions/import_error.html:10 +msgid "Missing required packages" +msgstr "" + +#: 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 " +"local_requirements.txt, and are normally installed as part of " +"the installation or upgrade process. To verify installed packages, run " +"pip freeze from the console and compare the output to the list " +"of required packages." +msgstr "" + +#: templates/exceptions/import_error.html:20 +msgid "WSGI service not restarted after upgrade" +msgstr "" + +#: 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 "" + +#: templates/exceptions/permission_error.html:6 +msgid "" +"A file permission error was detected while processing this request. Common " +"causes include the following:" +msgstr "" + +#: templates/exceptions/permission_error.html:10 +msgid "Insufficient write permission to the media root" +msgstr "" + +#: templates/exceptions/permission_error.html:11 +#, python-format +msgid "" +"The configured media root is %(media_root)s. Ensure that the " +"user NetBox runs as has access to write files to all locations within this " +"path." +msgstr "" + +#: templates/exceptions/programming_error.html:6 +msgid "" +"A database programming error was detected while processing this request. " +"Common causes include the following:" +msgstr "" + +#: templates/exceptions/programming_error.html:10 +msgid "Database migrations missing" +msgstr "" + +#: 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 "" + +#: templates/exceptions/programming_error.html:18 +msgid "Unsupported PostgreSQL version" +msgstr "" + +#: 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 "" + +#: templates/extras/admin/plugins_list.html:4 +#: templates/extras/admin/plugins_list.html:9 +#: templates/extras/admin/plugins_list.html:13 +msgid "Installed Plugins" +msgstr "" + +#: templates/extras/admin/plugins_list.html:23 +msgid "Package Name" +msgstr "" + +#: templates/extras/admin/plugins_list.html:24 +msgid "Author" +msgstr "" + +#: templates/extras/admin/plugins_list.html:25 +msgid "Author Email" +msgstr "" + +#: templates/extras/admin/plugins_list.html:27 +msgid "Version" +msgstr "" + +#: templates/extras/configcontext.html:46 +#: templates/extras/configtemplate.html:38 +#: templates/extras/exporttemplate.html:57 +msgid "The data file associated with this object has been deleted" +msgstr "" + +#: templates/extras/configcontext.html:55 +#: templates/extras/configtemplate.html:47 +#: templates/extras/exporttemplate.html:66 +msgid "Data Synced" +msgstr "" + +#: templates/extras/configcontext_list.html:7 +#: templates/extras/configtemplate_list.html:7 +#: templates/extras/exporttemplate_list.html:7 +msgid "Sync Data" +msgstr "" + +#: templates/extras/configrevision.html:47 +msgid "Default unit height" +msgstr "" + +#: templates/extras/configrevision.html:51 +msgid "Default unit width" +msgstr "" + +#: templates/extras/configrevision.html:63 +msgid "Default voltage" +msgstr "" + +#: templates/extras/configrevision.html:67 +msgid "Default amperage" +msgstr "" + +#: templates/extras/configrevision.html:71 +msgid "Default max utilization" +msgstr "" + +#: templates/extras/configrevision.html:83 +msgid "Enforce global unique" +msgstr "" + +#: templates/extras/configrevision.html:135 +msgid "Paginate count" +msgstr "" + +#: templates/extras/configrevision.html:139 +msgid "Max page size" +msgstr "" + +#: templates/extras/configrevision.html:163 +msgid "Default user preferences" +msgstr "" + +#: templates/extras/configrevision.html:187 +msgid "Job retention" +msgstr "" + +#: templates/extras/configrevision.html:199 +msgid "Comment" +msgstr "" + +#: templates/extras/configrevision_restore.html:8 +#: templates/extras/configrevision_restore.html:43 +#: templates/extras/configrevision_restore.html:79 +msgid "Restore" +msgstr "" + +#: templates/extras/configrevision_restore.html:21 +msgid "Config revisions" +msgstr "" + +#: templates/extras/configrevision_restore.html:54 +msgid "Parameter" +msgstr "" + +#: templates/extras/configrevision_restore.html:55 +msgid "Current Value" +msgstr "" + +#: templates/extras/configrevision_restore.html:56 +msgid "New Value" +msgstr "" + +#: templates/extras/configrevision_restore.html:66 +msgid "Changed" +msgstr "" + +#: templates/extras/configtemplate.html:58 +msgid "Environment Parameters" +msgstr "" + +#: templates/extras/configtemplate.html:69 +#: templates/extras/exporttemplate.html:88 +msgid "Template" +msgstr "" + +#: templates/extras/customfield.html:31 templates/extras/customlink.html:22 +msgid "Group Name" +msgstr "" + +#: templates/extras/customfield.html:43 +msgid "Cloneable" +msgstr "" + +#: templates/extras/customfield.html:53 +msgid "Default Value" +msgstr "" + +#: templates/extras/customfield.html:64 +msgid "Search Weight" +msgstr "" + +#: templates/extras/customfield.html:74 +msgid "Filter Logic" +msgstr "" + +#: templates/extras/customfield.html:78 +msgid "Display Weight" +msgstr "" + +#: templates/extras/customfield.html:104 +msgid "Validation Rules" +msgstr "" + +#: templates/extras/customfield.html:108 +msgid "Minimum Value" +msgstr "" + +#: templates/extras/customfield.html:112 +msgid "Maximum Value" +msgstr "" + +#: templates/extras/customfield.html:116 +msgid "Regular Expression" +msgstr "" + +#: templates/extras/customlink.html:30 +msgid "Button Class" +msgstr "" + +#: templates/extras/customlink.html:41 templates/extras/exporttemplate.html:73 +#: templates/extras/savedfilter.html:41 templates/extras/webhook.html:102 +msgid "Assigned Models" +msgstr "" + +#: templates/extras/customlink.html:57 +msgid "Link Text" +msgstr "" + +#: templates/extras/customlink.html:65 +msgid "Link URL" +msgstr "" + +#: templates/extras/dashboard/reset.html:4 templates/home.html:63 +msgid "Reset Dashboard" +msgstr "" + +#: templates/extras/dashboard/reset.html:8 +msgid "" +"This will remove all configured widgets and restore the " +"default dashboard configuration." +msgstr "" + +#: templates/extras/dashboard/reset.html:13 +msgid "" +"This change affects only your dashboard, and will not impact other " +"users." +msgstr "" + +#: templates/extras/dashboard/widget_add.html:7 +msgid "Add a Widget" +msgstr "" + +#: templates/extras/dashboard/widgets/bookmarks.html:14 +msgid "No bookmarks have been added yet." +msgstr "" + +#: templates/extras/dashboard/widgets/objectcounts.html:15 +msgid "No permission" +msgstr "" + +#: templates/extras/dashboard/widgets/objectlist.html:6 +msgid "No permission to view this content" +msgstr "" + +#: templates/extras/dashboard/widgets/objectlist.html:10 +msgid "Unable to load content. Invalid view name" +msgstr "" + +#: templates/extras/dashboard/widgets/rssfeed.html:12 +msgid "No content found" +msgstr "" + +#: templates/extras/dashboard/widgets/rssfeed.html:18 +msgid "There was a problem fetching the RSS feed" +msgstr "" + +#: templates/extras/dashboard/widgets/rssfeed.html:21 +msgid "HTTP" +msgstr "" + +#: templates/extras/exporttemplate.html:29 +msgid "MIME Type" +msgstr "" + +#: templates/extras/exporttemplate.html:33 +msgid "File Extension" +msgstr "" + +#: templates/extras/htmx/report_result.html:9 +#: templates/extras/htmx/script_result.html:10 +msgid "Scheduled for" +msgstr "" + +#: templates/extras/htmx/report_result.html:14 +#: templates/extras/htmx/script_result.html:15 +msgid "Duration" +msgstr "" + +#: templates/extras/htmx/report_result.html:20 +msgid "Report Methods" +msgstr "" + +#: templates/extras/htmx/report_result.html:38 +msgid "Report Results" +msgstr "" + +#: templates/extras/htmx/report_result.html:44 +#: templates/extras/htmx/script_result.html:26 +msgid "Level" +msgstr "" + +#: templates/extras/htmx/report_result.html:46 +#: templates/extras/htmx/script_result.html:27 +msgid "Message" +msgstr "" + +#: templates/extras/htmx/script_result.html:21 +msgid "Script Log" +msgstr "" + +#: templates/extras/htmx/script_result.html:25 +msgid "Line" +msgstr "" + +#: templates/extras/htmx/script_result.html:38 +msgid "No log output" +msgstr "" + +#: templates/extras/htmx/script_result.html:46 +msgid "Exec Time" +msgstr "" + +#: templates/extras/htmx/script_result.html:46 +msgctxt "Unit of time" +msgid "seconds" +msgstr "" + +#: templates/extras/htmx/script_result.html:50 +msgid "Output" +msgstr "" + +#: templates/extras/inc/result_pending.html:4 +msgid "Loading" +msgstr "" + +#: templates/extras/inc/result_pending.html:6 +msgid "Results pending" +msgstr "" + +#: templates/extras/journalentry.html:16 +msgid "Journal Entry" +msgstr "" + +#: templates/extras/object_changelog.html:15 +#: templates/extras/objectchange_list.html:9 +msgid "Change log retention" +msgstr "" + +#: templates/extras/object_changelog.html:15 +#: templates/extras/objectchange_list.html:9 +msgid "days" +msgstr "" + +#: templates/extras/object_changelog.html:15 +#: templates/extras/objectchange_list.html:9 +msgid "Indefinite" +msgstr "" + +#: templates/extras/object_configcontext.html:11 +msgid "Rendered Context" +msgstr "" + +#: templates/extras/object_configcontext.html:22 +msgid "Local Context" +msgstr "" + +#: templates/extras/object_configcontext.html:34 +msgid "The local config context overwrites all source contexts" +msgstr "" + +#: templates/extras/object_configcontext.html:40 +msgid "Source Contexts" +msgstr "" + +#: templates/extras/object_journal.html:18 +msgid "New Journal Entry" +msgstr "" + +#: templates/extras/objectchange.html:29 +#: templates/users/objectpermission.html:45 +msgid "Change" +msgstr "" + +#: templates/extras/objectchange.html:84 +msgid "Difference" +msgstr "" + +#: templates/extras/objectchange.html:87 +msgid "Previous" +msgstr "" + +#: templates/extras/objectchange.html:90 +msgid "Next" +msgstr "" + +#: templates/extras/objectchange.html:98 +msgid "Object Created" +msgstr "" + +#: templates/extras/objectchange.html:100 +msgid "Object Deleted" +msgstr "" + +#: templates/extras/objectchange.html:102 +msgid "No Changes" +msgstr "" + +#: templates/extras/objectchange.html:117 +msgid "Pre-Change Data" +msgstr "" + +#: templates/extras/objectchange.html:126 +msgid "Warning: Comparing non-atomic change to previous change record" +msgstr "" + +#: templates/extras/objectchange.html:136 +msgid "Post-Change Data" +msgstr "" + +#: templates/extras/objectchange.html:157 +#, python-format +msgid "See All %(count)s Changes" +msgstr "" + +#: templates/extras/report.html:14 +msgid "This report is invalid and cannot be run." +msgstr "" + +#: templates/extras/report.html:23 templates/extras/report_list.html:88 +msgid "Run Again" +msgstr "" + +#: templates/extras/report.html:25 templates/extras/report_list.html:90 +msgid "Run Report" +msgstr "" + +#: templates/extras/report.html:36 +msgid "Last run" +msgstr "" + +#: templates/extras/report/base.html:30 +msgid "Report" +msgstr "" + +#: templates/extras/report_list.html:48 templates/extras/script_list.html:54 +msgid "Last Run" +msgstr "" + +#: templates/extras/report_list.html:70 templates/extras/script_list.html:77 +msgid "Never" +msgstr "" + +#: templates/extras/report_list.html:75 +msgid "Report has no test methods" +msgstr "" + +#: templates/extras/report_list.html:76 +msgid "Invalid" +msgstr "" + +#: templates/extras/report_list.html:125 +msgid "No Reports Found" +msgstr "" + +#: templates/extras/report_list.html:128 +#, python-format +msgid "" +"Get started by creating a report from " +"an uploaded file or data source." +msgstr "" + +#: templates/extras/script.html:13 +msgid "You do not have permission to run scripts" +msgstr "" + +#: templates/extras/script.html:37 +msgid "Run Script" +msgstr "" + +#: templates/extras/script/base.html:29 +msgid "Script" +msgstr "" + +#: templates/extras/script_list.html:44 +#, python-format +msgid "" +"Script file at %(file_path)s could not be loaded." +msgstr "" + +#: templates/extras/script_list.html:91 +msgid "No Scripts Found" +msgstr "" + +#: templates/extras/script_list.html:94 +#, python-format +msgid "" +"Get started by creating a script from " +"an uploaded file or data source." +msgstr "" + +#: templates/extras/script_result.html:42 +msgid "Log" +msgstr "" + +#: templates/extras/tag.html:35 +msgid "Tagged Items" +msgstr "" + +#: templates/extras/tag.html:47 +msgid "Allowed Object Types" +msgstr "" + +#: templates/extras/tag.html:56 +msgid "Any" +msgstr "" + +#: templates/extras/tag.html:63 +msgid "Tagged Item Types" +msgstr "" + +#: templates/extras/tag.html:89 +msgid "Tagged Objects" +msgstr "" + +#: templates/extras/webhook.html:45 +msgid "Job start" +msgstr "" + +#: templates/extras/webhook.html:49 +msgid "Job end" +msgstr "" + +#: templates/extras/webhook.html:62 +msgid "HTTP Method" +msgstr "" + +#: templates/extras/webhook.html:70 +msgid "HTTP Content Type" +msgstr "" + +#: templates/extras/webhook.html:87 +msgid "SSL Verification" +msgstr "" + +#: templates/extras/webhook.html:128 +msgid "Additional Headers" +msgstr "" + +#: templates/extras/webhook.html:140 +msgid "Body Template" +msgstr "" + +#: templates/generic/bulk_add_component.html:15 +msgid "Bulk Creation" +msgstr "" + +#: templates/generic/bulk_add_component.html:20 +#: templates/generic/bulk_edit.html:28 +msgid "Selected Objects" +msgstr "" + +#: templates/generic/bulk_add_component.html:46 +msgid "to Add" +msgstr "" + +#: templates/generic/bulk_delete.html:24 +msgid "Confirm Bulk Deletion" +msgstr "" + +#: templates/generic/bulk_delete.html:26 +msgctxt "Noun" +msgid "Warning" +msgstr "" + +#: templates/generic/bulk_delete.html:27 +#, python-format +msgid "" +"The following operation will delete %(count)s " +"%(type_plural)s. Please carefully review the objects to be deleted and " +"confirm below." +msgstr "" + +#: templates/generic/bulk_edit.html:16 templates/generic/object_edit.html:17 +msgid "Editing" +msgstr "" + +#: templates/generic/bulk_edit.html:23 +msgid "Bulk Edit" +msgstr "" + +#: templates/generic/bulk_edit.html:124 templates/generic/bulk_rename.html:42 +msgid "Apply" +msgstr "" + +#: templates/generic/bulk_import.html:14 +msgid "Bulk Import" +msgstr "" + +#: templates/generic/bulk_import.html:20 +msgid "Direct Import" +msgstr "" + +#: templates/generic/bulk_import.html:25 +msgid "Upload File" +msgstr "" + +#: templates/generic/bulk_import.html:51 templates/generic/bulk_import.html:73 +#: templates/generic/bulk_import.html:95 +msgid "Submit" +msgstr "" + +#: templates/generic/bulk_import.html:110 +msgid "Field Options" +msgstr "" + +#: templates/generic/bulk_import.html:117 +msgid "Accessor" +msgstr "" + +#: templates/generic/bulk_import.html:154 +msgid "Import Value" +msgstr "" + +#: templates/generic/bulk_import.html:181 +msgid "Format: YYYY-MM-DD" +msgstr "" + +#: templates/generic/bulk_import.html:183 +msgid "Specify true or false" +msgstr "" + +#: templates/generic/bulk_import.html:195 +msgid "Required fields must be specified for all objects." +msgstr "" + +#: 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 "" + +#: templates/generic/bulk_remove.html:13 +msgid "Confirm Bulk Removal" +msgstr "" + +#: templates/generic/bulk_remove.html:15 +#, python-format +msgid "" +"Warning: The following operation will remove %(count)s " +"%(obj_type_plural)s from %(parent_obj)s." +msgstr "" + +#: templates/generic/bulk_remove.html:21 +#, python-format +msgid "" +"Please carefully review the %(obj_type_plural)s to be removed and confirm " +"below." +msgstr "" + +#: templates/generic/bulk_remove.html:38 +#, python-format +msgid "Delete these %(count)s %(obj_type_plural)s" +msgstr "" + +#: templates/generic/bulk_rename.html:7 +msgid "Renaming" +msgstr "" + +#: templates/generic/bulk_rename.html:16 +msgid "Current Name" +msgstr "" + +#: templates/generic/bulk_rename.html:17 +msgid "New Name" +msgstr "" + +#: templates/generic/bulk_rename.html:40 +#: utilities/templates/widgets/markdown_input.html:11 +msgid "Preview" +msgstr "" + +#: templates/generic/confirmation_form.html:16 +msgid "Are you sure" +msgstr "" + +#: templates/generic/confirmation_form.html:19 +msgid "Confirm" +msgstr "" + +#: templates/generic/object.html:51 +msgid "ago" +msgstr "" + +#: templates/generic/object_children.html:27 +#: utilities/templates/buttons/bulk_edit.html:4 +msgid "Edit Selected" +msgstr "" + +#: templates/generic/object_children.html:41 +#: utilities/templates/buttons/bulk_delete.html:4 +msgid "Delete Selected" +msgstr "" + +#: templates/generic/object_edit.html:19 +#, python-format +msgid "Add a new %(object_type)s" +msgstr "" + +#: templates/generic/object_edit.html:47 +msgid "View model documentation" +msgstr "" + +#: templates/generic/object_edit.html:48 +msgid "Help" +msgstr "" + +#: templates/generic/object_edit.html:73 +msgid "Create & Add Another" +msgstr "" + +#: templates/generic/object_list.html:48 templates/search.html:13 +msgid "Results" +msgstr "" + +#: templates/generic/object_list.html:54 +msgid "Filters" +msgstr "" + +#: templates/generic/object_list.html:94 +#, python-format +msgid "" +"Select all %(count)s %(object_type_plural)s matching query" +msgstr "" + +#: templates/home.html:12 +msgid "New Release Available" +msgstr "" + +#: templates/home.html:14 +msgid "is available" +msgstr "" + +#: templates/home.html:17 +msgctxt "Document title" +msgid "Upgrade Instructions" +msgstr "" + +#: templates/home.html:37 +msgid "Unlock Dashboard" +msgstr "" + +#: templates/home.html:46 +msgid "Lock Dashboard" +msgstr "" + +#: templates/home.html:57 +msgid "Add Widget" +msgstr "" + +#: templates/home.html:60 +msgid "Save Layout" +msgstr "" + +#: templates/htmx/delete_form.html:7 +msgid "Confirm Deletion" +msgstr "" + +#: templates/htmx/delete_form.html:11 +#, python-format +msgid "" +"Are you sure you want to delete " +"%(object_type)s %(object)s?" +msgstr "" + +#: templates/htmx/object_selector.html:5 +msgid "Select" +msgstr "" + +#: templates/inc/filter_list.html:50 +#: utilities/templates/helpers/table_config_form.html:39 +msgid "Reset" +msgstr "" + +#: templates/inc/missing_prerequisites.html:7 +#, python-format +msgid "" +"Before you can add a %(model)s you must first create a " +"%(prerequisite_model)s." +msgstr "" + +#: templates/inc/paginator.html:38 templates/inc/paginator_htmx.html:53 +msgid "Per Page" +msgstr "" + +#: templates/inc/paginator.html:49 templates/inc/paginator_htmx.html:69 +#, python-format +msgid "Showing %(start)s-%(end)s of %(total)s" +msgstr "" + +#: templates/inc/panels/image_attachments.html:10 +msgid "Attach an image" +msgstr "" + +#: templates/inc/panels/related_objects.html:5 +msgid "Related Objects" +msgstr "" + +#: templates/inc/panels/tags.html:11 +msgid "No tags assigned" +msgstr "" + +#: templates/inc/profile_button.html:12 templates/inc/profile_button.html:62 +msgid "Dark Mode" +msgstr "" + +#: templates/inc/profile_button.html:45 +msgid "Log Out" +msgstr "" + +#: templates/inc/profile_button.html:53 +msgid "Log In" +msgstr "" + +#: templates/inc/sync_warning.html:7 +msgid "Data is out of sync with upstream file" +msgstr "" + +#: templates/inc/table_controls_htmx.html:16 +#: templates/inc/table_controls_htmx.html:18 +msgid "Configure Table" +msgstr "" + +#: templates/ipam/aggregate.html:15 templates/ipam/ipaddress.html:17 +#: templates/ipam/iprange.html:16 templates/ipam/prefix.html:15 +msgid "Family" +msgstr "" + +#: templates/ipam/aggregate.html:40 +msgid "Date Added" +msgstr "" + +#: templates/ipam/aggregate/prefixes.html:8 +#: templates/ipam/prefix/prefixes.html:8 templates/ipam/role.html:10 +msgid "Add Prefix" +msgstr "" + +#: templates/ipam/asn.html:24 +msgid "AS Number" +msgstr "" + +#: templates/ipam/fhrpgroup.html:55 +msgid "Authentication Type" +msgstr "" + +#: templates/ipam/fhrpgroup.html:59 +msgid "Authentication Key" +msgstr "" + +#: templates/ipam/fhrpgroup.html:72 +msgid "Virtual IP Addresses" +msgstr "" + +#: templates/ipam/fhrpgroupassignment_edit.html:8 +msgid "FHRP Group Assignment" +msgstr "" + +#: templates/ipam/inc/ipaddress_edit_header.html:19 +msgid "Assign IP" +msgstr "" + +#: templates/ipam/inc/ipaddress_edit_header.html:28 +msgid "Bulk Create" +msgstr "" + +#: templates/ipam/inc/panels/fhrp_groups.html:12 +msgid "Virtual IPs" +msgstr "" + +#: templates/ipam/inc/panels/fhrp_groups.html:52 +msgid "Create Group" +msgstr "" + +#: templates/ipam/inc/panels/fhrp_groups.html:57 +msgid "Assign Group" +msgstr "" + +#: templates/ipam/inc/toggle_available.html:7 +msgid "Show Assigned" +msgstr "" + +#: templates/ipam/inc/toggle_available.html:10 +msgid "Show Available" +msgstr "" + +#: templates/ipam/inc/toggle_available.html:13 +msgid "Show All" +msgstr "" + +#: templates/ipam/ipaddress.html:26 templates/ipam/iprange.html:48 +#: templates/ipam/prefix.html:24 +msgid "Global" +msgstr "" + +#: templates/ipam/ipaddress.html:88 +msgid "NAT (outside)" +msgstr "" + +#: templates/ipam/ipaddress_assign.html:8 +msgid "Assign an IP Address" +msgstr "" + +#: templates/ipam/ipaddress_assign.html:23 +msgid "Select IP Address" +msgstr "" + +#: templates/ipam/ipaddress_assign.html:39 +msgid "Search Results" +msgstr "" + +#: templates/ipam/ipaddress_bulk_add.html:6 +msgid "Bulk Add IP Addresses" +msgstr "" + +#: templates/ipam/ipaddress_edit.html:35 +msgid "Interface Assignment" +msgstr "" + +#: templates/ipam/ipaddress_edit.html:74 +msgid "NAT IP (Inside" +msgstr "" + +#: templates/ipam/iprange.html:20 +msgid "Starting Address" +msgstr "" + +#: templates/ipam/iprange.html:24 +msgid "Ending Address" +msgstr "" + +#: templates/ipam/iprange.html:36 templates/ipam/prefix.html:104 +msgid "Marked fully utilized" +msgstr "" + +#: templates/ipam/l2vpn.html:11 templates/ipam/l2vpntermination.html:10 +msgid "L2VPN Attributes" +msgstr "" + +#: templates/ipam/l2vpn.html:65 +msgid "Add a Termination" +msgstr "" + +#: templates/ipam/l2vpntermination_edit.html:9 +msgid "L2VPN Termination" +msgstr "" + +#: templates/ipam/prefix.html:112 +msgid "Child IPs" +msgstr "" + +#: templates/ipam/prefix.html:120 +msgid "Available IPs" +msgstr "" + +#: templates/ipam/prefix.html:132 +msgid "First available IP" +msgstr "" + +#: templates/ipam/prefix.html:151 +msgid "Addressing Details" +msgstr "" + +#: templates/ipam/prefix.html:181 +msgid "Prefix Details" +msgstr "" + +#: templates/ipam/prefix.html:187 +msgid "Network Address" +msgstr "" + +#: templates/ipam/prefix.html:191 +msgid "Network Mask" +msgstr "" + +#: templates/ipam/prefix.html:195 +msgid "Wildcard Mask" +msgstr "" + +#: templates/ipam/prefix.html:199 +msgid "Broadcast Address" +msgstr "" + +#: templates/ipam/prefix/ip_ranges.html:7 +msgid "Add IP Range" +msgstr "" + +#: templates/ipam/prefix_list.html:7 +msgid "Hide Depth Indicators" +msgstr "" + +#: templates/ipam/prefix_list.html:11 +msgid "Max Depth" +msgstr "" + +#: templates/ipam/prefix_list.html:28 +msgid "Max Length" +msgstr "" + +#: templates/ipam/rir.html:10 +msgid "Add Aggregate" +msgstr "" + +#: templates/ipam/routetarget.html:10 +msgid "Route Target" +msgstr "" + +#: templates/ipam/routetarget.html:40 +msgid "Importing VRFs" +msgstr "" + +#: templates/ipam/routetarget.html:49 +msgid "Exporting VRFs" +msgstr "" + +#: templates/ipam/routetarget.html:60 +msgid "Importing L2VPNs" +msgstr "" + +#: templates/ipam/routetarget.html:69 +msgid "Exporting L2VPNs" +msgstr "" + +#: templates/ipam/service.html:22 templates/ipam/service_create.html:8 +#: templates/ipam/service_edit.html:8 +msgid "Service" +msgstr "" + +#: templates/ipam/service_create.html:43 +msgid "From Template" +msgstr "" + +#: templates/ipam/service_create.html:48 +msgid "Custom" +msgstr "" + +#: templates/ipam/service_edit.html:37 +msgid "Port(s)" +msgstr "" + +#: templates/ipam/vlan.html:95 +msgid "Add a Prefix" +msgstr "" + +#: templates/ipam/vlangroup.html:18 +msgid "Add VLAN" +msgstr "" + +#: templates/ipam/vlangroup.html:43 +msgid "Permitted VIDs" +msgstr "" + +#: templates/ipam/vrf.html:19 +msgid "Route Distinguisher" +msgstr "" + +#: templates/ipam/vrf.html:32 +msgid "Unique IP Space" +msgstr "" + +#: templates/login.html:20 +#: utilities/templates/form_helpers/render_errors.html:7 +msgid "Errors" +msgstr "" + +#: templates/login.html:48 +msgid "Sign In" +msgstr "" + +#: templates/login.html:54 +msgid "Or use a single sign-on (SSO) provider" +msgstr "" + +#: templates/login.html:68 +msgid "Toggle Color Mode" +msgstr "" + +#: templates/media_failure.html:7 +msgid "Static Media Failure - NetBox" +msgstr "" + +#: templates/media_failure.html:21 +msgid "Static Media Failure" +msgstr "" + +#: templates/media_failure.html:23 +msgid "The following static media file failed to load" +msgstr "" + +#: templates/media_failure.html:26 +msgid "Check the following" +msgstr "" + +#: 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 "" + +#: templates/media_failure.html:35 +#, python-format +msgid "" +"The HTTP service (e.g. nginx or Apache) is configured to serve files from " +"the STATIC_ROOT path. Refer to the " +"installation documentation for further guidance." +msgstr "" + +#: 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 "" + +#: templates/media_failure.html:55 +#, python-format +msgid "" +"Click here to attempt loading NetBox again." +msgstr "" + +#: templates/tenancy/contact.html:18 tenancy/filtersets.py:123 +#: tenancy/forms/bulk_edit.py:136 tenancy/forms/filtersets.py:103 +#: tenancy/forms/forms.py:56 tenancy/forms/model_forms.py:112 +#: tenancy/forms/model_forms.py:135 tenancy/tables/contacts.py:98 +msgid "Contact" +msgstr "" + +#: templates/tenancy/contact.html:30 tenancy/forms/bulk_edit.py:98 +msgid "Title" +msgstr "" + +#: templates/tenancy/contact.html:34 tenancy/forms/bulk_edit.py:103 +#: tenancy/tables/contacts.py:64 +msgid "Phone" +msgstr "" + +#: templates/tenancy/contact.html:86 tenancy/tables/contacts.py:73 +msgid "Assignments" +msgstr "" + +#: templates/tenancy/contactassignment_edit.html:12 +msgid "Contact Assignment" +msgstr "" + +#: templates/tenancy/contactgroup.html:19 tenancy/forms/forms.py:66 +#: tenancy/forms/model_forms.py:79 +msgid "Contact Group" +msgstr "" + +#: templates/tenancy/contactgroup.html:57 +msgid "Add Contact Group" +msgstr "" + +#: templates/tenancy/contactrole.html:15 tenancy/filtersets.py:128 +#: tenancy/forms/forms.py:61 tenancy/forms/model_forms.py:93 +msgid "Contact Role" +msgstr "" + +#: templates/tenancy/object_contacts.html:9 +msgid "Add a contact" +msgstr "" + +#: templates/tenancy/tenantgroup.html:17 +msgid "Add Tenant" +msgstr "" + +#: templates/tenancy/tenantgroup.html:27 tenancy/forms/model_forms.py:34 +#: tenancy/tables/columns.py:51 tenancy/tables/columns.py:61 +msgid "Tenant Group" +msgstr "" + +#: templates/tenancy/tenantgroup.html:66 +msgid "Add Tenant Group" +msgstr "" + +#: templates/users/group.html:37 templates/users/user.html:61 +msgid "Assigned Permissions" +msgstr "" + +#: templates/users/objectpermission.html:6 +#: templates/users/objectpermission.html:14 users/forms/filtersets.py:69 +msgid "Permission" +msgstr "" + +#: templates/users/objectpermission.html:33 users/forms/filtersets.py:70 +#: users/forms/model_forms.py:321 +msgid "Actions" +msgstr "" + +#: templates/users/objectpermission.html:37 +msgid "View" +msgstr "" + +#: templates/users/objectpermission.html:56 users/forms/model_forms.py:324 +msgid "Constraints" +msgstr "" + +#: templates/users/objectpermission.html:76 +msgid "Assigned Users" +msgstr "" + +#: templates/users/user.html:38 +msgid "Staff" +msgstr "" + +#: templates/virtualization/cluster.html:56 +msgid "Allocated Resources" +msgstr "" + +#: templates/virtualization/cluster.html:60 +#: templates/virtualization/virtualmachine.html:128 +msgid "Virtual CPUs" +msgstr "" + +#: templates/virtualization/cluster.html:64 +#: templates/virtualization/virtualmachine.html:132 +msgid "Memory" +msgstr "" + +#: templates/virtualization/cluster.html:74 +#: templates/virtualization/virtualmachine.html:142 +msgid "Disk Space" +msgstr "" + +#: templates/virtualization/cluster.html:77 +#: templates/virtualization/virtualmachine.html:145 +msgctxt "Abbreviation for gigabyte" +msgid "GB" +msgstr "" + +#: templates/virtualization/cluster/base.html:18 +msgid "Add Virtual Machine" +msgstr "" + +#: templates/virtualization/cluster/base.html:24 +msgid "Assign Device" +msgstr "" + +#: templates/virtualization/cluster/devices.html:10 +msgid "Remove Selected" +msgstr "" + +#: templates/virtualization/cluster_add_devices.html:9 +#, python-format +msgid "Add Device to Cluster %(cluster)s" +msgstr "" + +#: templates/virtualization/cluster_add_devices.html:23 +msgid "Device Selection" +msgstr "" + +#: templates/virtualization/cluster_add_devices.html:31 +msgid "Add Devices" +msgstr "" + +#: templates/virtualization/clustergroup.html:10 +#: templates/virtualization/clustertype.html:10 +msgid "Add Cluster" +msgstr "" + +#: templates/virtualization/clustergroup.html:20 +#: virtualization/forms/model_forms.py:50 +msgid "Cluster Group" +msgstr "" + +#: templates/virtualization/clustertype.html:20 +#: templates/virtualization/virtualmachine.html:111 +#: virtualization/forms/model_forms.py:34 +msgid "Cluster Type" +msgstr "" + +#: templates/virtualization/virtualmachine.html:124 +#: virtualization/forms/bulk_edit.py:187 +#: virtualization/forms/model_forms.py:225 +msgid "Resources" +msgstr "" + +#: templates/wireless/inc/authentication_attrs.html:13 +msgid "Cipher" +msgstr "" + +#: templates/wireless/inc/authentication_attrs.html:17 +msgid "PSK" +msgstr "" + +#: templates/wireless/inc/authentication_attrs.html:21 +msgid "Show Secret" +msgstr "" + +#: templates/wireless/inc/wirelesslink_interface.html:35 +#: templates/wireless/inc/wirelesslink_interface.html:45 +msgctxt "Abbreviation for megahertz" +msgid "MHz" +msgstr "" + +#: templates/wireless/wirelesslan.html:11 wireless/forms/model_forms.py:54 +msgid "Wireless LAN" +msgstr "" + +#: templates/wireless/wirelesslan.html:59 +msgid "Attached Interfaces" +msgstr "" + +#: templates/wireless/wirelesslangroup.html:17 +msgid "Add Wireless LAN" +msgstr "" + +#: templates/wireless/wirelesslangroup.html:26 wireless/forms/model_forms.py:27 +msgid "Wireless LAN Group" +msgstr "" + +#: templates/wireless/wirelesslangroup.html:64 +msgid "Add Wireless LAN Group" +msgstr "" + +#: templates/wireless/wirelesslink.html:16 +msgid "Link Properties" +msgstr "" + +#: tenancy/choices.py:19 +msgid "Tertiary" +msgstr "" + +#: tenancy/choices.py:20 +msgid "Inactive" +msgstr "" + +#: tenancy/filtersets.py:30 tenancy/filtersets.py:56 +msgid "Contact group (ID)" +msgstr "" + +#: tenancy/filtersets.py:36 tenancy/filtersets.py:63 +msgid "Contact group (slug)" +msgstr "" + +#: tenancy/filtersets.py:92 +msgid "Contact (ID)" +msgstr "" + +#: tenancy/filtersets.py:96 +msgid "Contact role (ID)" +msgstr "" + +#: tenancy/filtersets.py:102 +msgid "Contact role (slug)" +msgstr "" + +#: tenancy/filtersets.py:134 +msgid "Contact group" +msgstr "" + +#: tenancy/filtersets.py:145 tenancy/filtersets.py:164 +msgid "Tenant group (ID)" +msgstr "" + +#: tenancy/filtersets.py:197 +msgid "Tenant Group (ID)" +msgstr "" + +#: tenancy/filtersets.py:204 +msgid "Tenant Group (slug)" +msgstr "" + +#: tenancy/forms/bulk_edit.py:65 +msgid "Desciption" +msgstr "" + +#: tenancy/forms/bulk_import.py:101 +msgid "Assigned contact" +msgstr "" + +#: tenancy/models/contacts.py:31 +msgid "contact group" +msgstr "" + +#: tenancy/models/contacts.py:32 +msgid "contact groups" +msgstr "" + +#: tenancy/models/contacts.py:47 +msgid "contact role" +msgstr "" + +#: tenancy/models/contacts.py:48 +msgid "contact roles" +msgstr "" + +#: tenancy/models/contacts.py:67 +msgid "title" +msgstr "" + +#: tenancy/models/contacts.py:72 +msgid "phone" +msgstr "" + +#: tenancy/models/contacts.py:77 +msgid "email" +msgstr "" + +#: tenancy/models/contacts.py:86 +msgid "link" +msgstr "" + +#: tenancy/models/contacts.py:102 +msgid "contact" +msgstr "" + +#: tenancy/models/contacts.py:103 +msgid "contacts" +msgstr "" + +#: tenancy/models/contacts.py:149 +msgid "contact assignment" +msgstr "" + +#: tenancy/models/contacts.py:150 +msgid "contact assignments" +msgstr "" + +#: tenancy/models/tenants.py:32 +msgid "tenant group" +msgstr "" + +#: tenancy/models/tenants.py:33 +msgid "tenant groups" +msgstr "" + +#: tenancy/models/tenants.py:70 +msgid "Tenant name must be unique per group." +msgstr "" + +#: tenancy/models/tenants.py:80 +msgid "Tenant slug must be unique per group." +msgstr "" + +#: tenancy/models/tenants.py:88 +msgid "tenant" +msgstr "" + +#: tenancy/models/tenants.py:89 +msgid "tenants" +msgstr "" + +#: tenancy/tables/contacts.py:107 +msgid "Contact Title" +msgstr "" + +#: tenancy/tables/contacts.py:111 +msgid "Contact Phone" +msgstr "" + +#: tenancy/tables/contacts.py:115 +msgid "Contact Email" +msgstr "" + +#: tenancy/tables/contacts.py:119 +msgid "Contact Address" +msgstr "" + +#: tenancy/tables/contacts.py:123 +msgid "Contact Link" +msgstr "" + +#: tenancy/tables/contacts.py:127 +msgid "Contact Description" +msgstr "" + +#: users/filtersets.py:48 users/filtersets.py:151 +msgid "Group (name)" +msgstr "" + +#: users/forms/bulk_edit.py:24 +msgid "First name" +msgstr "" + +#: users/forms/bulk_edit.py:29 +msgid "Last name" +msgstr "" + +#: users/forms/bulk_edit.py:41 +msgid "Staff status" +msgstr "" + +#: users/forms/bulk_edit.py:46 +msgid "Superuser status" +msgstr "" + +#: users/forms/bulk_import.py:43 +msgid "If no key is provided, one will be generated automatically." +msgstr "" + +#: users/forms/filtersets.py:54 users/tables.py:42 +msgid "Is Staff" +msgstr "" + +#: users/forms/filtersets.py:61 users/tables.py:45 +msgid "Is Superuser" +msgstr "" + +#: users/forms/filtersets.py:94 users/tables.py:89 +msgid "Can View" +msgstr "" + +#: users/forms/filtersets.py:101 users/tables.py:92 +msgid "Can Add" +msgstr "" + +#: users/forms/filtersets.py:108 users/tables.py:95 +msgid "Can Change" +msgstr "" + +#: users/forms/filtersets.py:115 users/tables.py:98 +msgid "Can Delete" +msgstr "" + +#: users/forms/model_forms.py:58 +msgid "User Interface" +msgstr "" + +#: users/forms/model_forms.py:115 +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 "" + +#: users/forms/model_forms.py:127 +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 "" + +#: users/forms/model_forms.py:176 +msgid "Confirm password" +msgstr "" + +#: users/forms/model_forms.py:179 +msgid "Enter the same password as before, for verification." +msgstr "" + +#: users/forms/model_forms.py:237 +msgid "Passwords do not match! Please check your input and try again." +msgstr "" + +#: users/forms/model_forms.py:303 +msgid "Additional actions" +msgstr "" + +#: users/forms/model_forms.py:306 +msgid "Actions granted in addition to those listed above" +msgstr "" + +#: users/forms/model_forms.py:322 +msgid "Objects" +msgstr "" + +#: users/forms/model_forms.py:334 +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 "" + +#: users/forms/model_forms.py:372 +msgid "At least one action must be selected." +msgstr "" + +#: users/forms/model_forms.py:389 +#, python-brace-format +msgid "Invalid filter for {model}: {error}" +msgstr "" + +#: users/models.py:54 +msgid "user" +msgstr "" + +#: users/models.py:55 +msgid "users" +msgstr "" + +#: users/models.py:66 +msgid "A user with this username already exists." +msgstr "" + +#: users/models.py:78 +msgid "group" +msgstr "" + +#: users/models.py:79 +msgid "groups" +msgstr "" + +#: users/models.py:104 users/models.py:105 +msgid "user preferences" +msgstr "" + +#: users/models.py:172 +#, python-brace-format +msgid "Key '{path}' is a leaf node; cannot assign new keys" +msgstr "" + +#: users/models.py:184 +#, python-brace-format +msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" +msgstr "" + +#: users/models.py:249 +msgid "expires" +msgstr "" + +#: users/models.py:254 +msgid "last used" +msgstr "" + +#: users/models.py:259 +msgid "key" +msgstr "" + +#: users/models.py:265 +msgid "write enabled" +msgstr "" + +#: users/models.py:267 +msgid "Permit create/update/delete operations using this key" +msgstr "" + +#: users/models.py:278 +msgid "allowed IPs" +msgstr "" + +#: users/models.py:280 +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 "" + +#: users/models.py:288 +msgid "token" +msgstr "" + +#: users/models.py:289 +msgid "tokens" +msgstr "" + +#: users/models.py:370 +msgid "The list of actions granted by this permission" +msgstr "" + +#: users/models.py:375 +msgid "constraints" +msgstr "" + +#: users/models.py:376 +msgid "Queryset filter matching the applicable objects of the selected type(s)" +msgstr "" + +#: users/models.py:383 +msgid "permission" +msgstr "" + +#: users/models.py:384 +msgid "permissions" +msgstr "" + +#: users/tables.py:101 +msgid "Custom Actions" +msgstr "" + +#: utilities/choices.py:16 +#, python-brace-format +msgid "{name} has a key defined but CHOICES is not a list" +msgstr "" + +#: utilities/choices.py:135 +msgid "Dark Red" +msgstr "" + +#: utilities/choices.py:138 +msgid "Rose" +msgstr "" + +#: utilities/choices.py:139 +msgid "Fuchsia" +msgstr "" + +#: utilities/choices.py:141 +msgid "Dark Purple" +msgstr "" + +#: utilities/choices.py:144 +msgid "Light Blue" +msgstr "" + +#: utilities/choices.py:147 +msgid "Aqua" +msgstr "" + +#: utilities/choices.py:148 +msgid "Dark Green" +msgstr "" + +#: utilities/choices.py:150 +msgid "Light Green" +msgstr "" + +#: utilities/choices.py:151 +msgid "Lime" +msgstr "" + +#: utilities/choices.py:153 +msgid "Amber" +msgstr "" + +#: utilities/choices.py:155 +msgid "Dark Orange" +msgstr "" + +#: utilities/choices.py:156 +msgid "Brown" +msgstr "" + +#: utilities/choices.py:157 +msgid "Light Grey" +msgstr "" + +#: utilities/choices.py:158 +msgid "Grey" +msgstr "" + +#: utilities/choices.py:159 +msgid "Dark Grey" +msgstr "" + +#: utilities/choices.py:217 +msgid "Direct" +msgstr "" + +#: utilities/choices.py:218 +msgid "Upload" +msgstr "" + +#: utilities/choices.py:230 utilities/choices.py:244 +msgid "Auto-detect" +msgstr "" + +#: utilities/choices.py:245 +msgid "Comma" +msgstr "" + +#: utilities/choices.py:246 +msgid "Semicolon" +msgstr "" + +#: utilities/choices.py:247 +msgid "Tab" +msgstr "" + +#: utilities/fields.py:162 +#, python-format +msgid "" +"%s(%r) is invalid. to_model parameter to CounterCacheField must be a string " +"in the format 'app.model'" +msgstr "" + +#: utilities/fields.py:172 +#, python-format +msgid "" +"%s(%r) is invalid. to_field parameter to CounterCacheField must be a string " +"in the format 'field'" +msgstr "" + +#: utilities/forms/bulk_import.py:24 +msgid "Enter object data in CSV, JSON or YAML format." +msgstr "" + +#: utilities/forms/bulk_import.py:37 +msgid "CSV delimiter" +msgstr "" + +#: utilities/forms/bulk_import.py:38 +msgid "The character which delimits CSV fields. Applies only to CSV format." +msgstr "" + +#: utilities/forms/bulk_import.py:101 +msgid "Unable to detect data format. Please specify." +msgstr "" + +#: utilities/forms/bulk_import.py:124 +msgid "Invalid CSV delimiter" +msgstr "" + +#: utilities/forms/bulk_import.py:168 +msgid "" +"Invalid YAML data. Data must be in the form of multiple documents, or a " +"single document comprising a list of dictionaries." +msgstr "" + +#: utilities/forms/fields/array.py:17 +#, python-brace-format +msgid "" +"Invalid list ({value}). Must be numeric and ranges must be in ascending " +"order." +msgstr "" + +#: utilities/forms/fields/csv.py:44 +#, python-brace-format +msgid "Invalid value for a multiple choice field: {value}" +msgstr "" + +#: utilities/forms/fields/csv.py:57 utilities/forms/fields/csv.py:74 +#, python-format +msgid "Object not found: %(value)s" +msgstr "" + +#: utilities/forms/fields/csv.py:65 +#, python-brace-format +msgid "" +"\"{value}\" is not a unique value for this field; multiple objects were found" +msgstr "" + +#: utilities/forms/fields/csv.py:97 +msgid "Object type must be specified as \".\"" +msgstr "" + +#: utilities/forms/fields/csv.py:101 +msgid "Invalid object type" +msgstr "" + +#: 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 "" + +#: 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 "" + +#: utilities/forms/fields/fields.py:31 +#, python-brace-format +msgid "" +" Markdown syntax is supported" +msgstr "" + +#: utilities/forms/fields/fields.py:48 +msgid "URL-friendly unique shorthand" +msgstr "" + +#: utilities/forms/fields/fields.py:99 +msgid "Enter context data in JSON format." +msgstr "" + +#: utilities/forms/fields/fields.py:117 +msgid "MAC address must be in EUI-48 format" +msgstr "" + +#: utilities/forms/forms.py:53 +msgid "Use regular expressions" +msgstr "" + +#: utilities/forms/forms.py:87 +#, python-brace-format +msgid "Unrecognized header: {name}" +msgstr "" + +#: utilities/forms/forms.py:113 +msgid "Available Columns" +msgstr "" + +#: utilities/forms/forms.py:121 +msgid "Selected Columns" +msgstr "" + +#: utilities/forms/mixins.py:101 +msgid "" +"This object has been modified since the form was rendered. Please consult " +"the object's change log for details." +msgstr "" + +#: utilities/templates/builtins/customfield_value.html:30 +msgid "Not defined" +msgstr "" + +#: utilities/templates/buttons/bookmark.html:9 +msgid "Unbookmark" +msgstr "" + +#: utilities/templates/buttons/bookmark.html:13 +msgid "Bookmark" +msgstr "" + +#: utilities/templates/buttons/clone.html:4 +msgid "Clone" +msgstr "" + +#: utilities/templates/buttons/export.html:4 +msgid "Export" +msgstr "" + +#: utilities/templates/buttons/export.html:7 +msgid "Current View" +msgstr "" + +#: utilities/templates/buttons/export.html:8 +msgid "All Data" +msgstr "" + +#: utilities/templates/buttons/export.html:28 +msgid "Add export template" +msgstr "" + +#: utilities/templates/buttons/import.html:4 +msgid "Import" +msgstr "" + +#: utilities/templates/form_helpers/render_field.html:36 +msgid "Copy to clipboard" +msgstr "" + +#: utilities/templates/form_helpers/render_field.html:52 +msgid "This field is required" +msgstr "" + +#: utilities/templates/form_helpers/render_field.html:65 +msgid "Set Null" +msgstr "" + +#: utilities/templates/helpers/applied_filters.html:11 +msgid "Clear all" +msgstr "" + +#: utilities/templates/helpers/table_config_form.html:8 +msgid "Table Configuration" +msgstr "" + +#: utilities/templates/helpers/table_config_form.html:31 +msgid "Move Up" +msgstr "" + +#: utilities/templates/helpers/table_config_form.html:34 +msgid "Move Down" +msgstr "" + +#: utilities/templates/widgets/apiselect.html:7 +msgid "Open selector" +msgstr "" + +#: utilities/templates/widgets/clearable_file_input.html:12 +msgid "None assigned" +msgstr "" + +#: utilities/templates/widgets/markdown_input.html:6 +msgid "Write" +msgstr "" + +#: utilities/templates/widgets/markdown_input.html:20 +msgid "Testing" +msgstr "" + +#: virtualization/filtersets.py:77 +msgid "Parent group (ID)" +msgstr "" + +#: virtualization/filtersets.py:83 +msgid "Parent group (slug)" +msgstr "" + +#: virtualization/filtersets.py:87 virtualization/filtersets.py:137 +msgid "Cluster type (ID)" +msgstr "" + +#: virtualization/filtersets.py:126 +msgid "Cluster group (ID)" +msgstr "" + +#: virtualization/filtersets.py:147 virtualization/filtersets.py:262 +msgid "Cluster (ID)" +msgstr "" + +#: virtualization/forms/bulk_edit.py:163 +#: virtualization/models/virtualmachines.py:112 +msgid "vCPUs" +msgstr "" + +#: virtualization/forms/bulk_edit.py:167 +msgid "Memory (MB)" +msgstr "" + +#: virtualization/forms/bulk_edit.py:171 +msgid "Disk (GB)" +msgstr "" + +#: virtualization/forms/bulk_import.py:43 +msgid "Type of cluster" +msgstr "" + +#: virtualization/forms/bulk_import.py:50 +msgid "Assigned cluster group" +msgstr "" + +#: virtualization/forms/bulk_import.py:95 +msgid "Assigned cluster" +msgstr "" + +#: virtualization/forms/bulk_import.py:102 +msgid "Assigned device within cluster" +msgstr "" + +#: virtualization/forms/model_forms.py:155 +#, python-brace-format +msgid "" +"{device} belongs to a different site ({device_site}) than the cluster " +"({cluster_site})" +msgstr "" + +#: virtualization/forms/model_forms.py:194 +msgid "Optionally pin this VM to a specific host device within the cluster" +msgstr "" + +#: virtualization/forms/model_forms.py:222 +msgid "Site/Cluster" +msgstr "" + +#: virtualization/models/clusters.py:25 +msgid "cluster type" +msgstr "" + +#: virtualization/models/clusters.py:26 +msgid "cluster types" +msgstr "" + +#: virtualization/models/clusters.py:45 +msgid "cluster group" +msgstr "" + +#: virtualization/models/clusters.py:46 +msgid "cluster groups" +msgstr "" + +#: virtualization/models/clusters.py:121 +msgid "cluster" +msgstr "" + +#: virtualization/models/clusters.py:122 +msgid "clusters" +msgstr "" + +#: virtualization/models/clusters.py:141 +#, python-brace-format +msgid "" +"{count} devices are assigned as hosts for this cluster but are not in site " +"{site}" +msgstr "" + +#: virtualization/models/virtualmachines.py:120 +msgid "memory (MB)" +msgstr "" + +#: virtualization/models/virtualmachines.py:125 +msgid "disk (GB)" +msgstr "" + +#: virtualization/models/virtualmachines.py:154 +msgid "Virtual machine name must be unique per cluster." +msgstr "" + +#: virtualization/models/virtualmachines.py:157 +msgid "virtual machine" +msgstr "" + +#: virtualization/models/virtualmachines.py:158 +msgid "virtual machines" +msgstr "" + +#: virtualization/models/virtualmachines.py:172 +msgid "A virtual machine must be assigned to a site and/or cluster." +msgstr "" + +#: virtualization/models/virtualmachines.py:179 +#, python-brace-format +msgid "The selected cluster ({cluster}) is not assigned to this site ({site})." +msgstr "" + +#: virtualization/models/virtualmachines.py:186 +msgid "Must specify a cluster when assigning a host device." +msgstr "" + +#: virtualization/models/virtualmachines.py:191 +#, python-brace-format +msgid "" +"The selected device ({device}) is not assigned to this cluster ({cluster})." +msgstr "" + +#: virtualization/models/virtualmachines.py:204 +#, python-brace-format +msgid "Must be an IPv{family} address. ({ip} is an IPv{version} address.)" +msgstr "" + +#: virtualization/models/virtualmachines.py:213 +#, python-brace-format +msgid "The specified IP address ({ip}) is not assigned to this VM." +msgstr "" + +#: virtualization/models/virtualmachines.py:331 +#, python-brace-format +msgid "" +"The selected parent interface ({parent}) belongs to a different virtual " +"machine ({virtual_machine})." +msgstr "" + +#: virtualization/models/virtualmachines.py:346 +#, python-brace-format +msgid "" +"The selected bridge interface ({bridge}) belongs to a different virtual " +"machine ({virtual_machine})." +msgstr "" + +#: virtualization/models/virtualmachines.py:357 +#, 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 "" + +#: wireless/choices.py:11 +msgid "Access point" +msgstr "" + +#: wireless/choices.py:12 +msgid "Station" +msgstr "" + +#: wireless/choices.py:467 +msgid "Open" +msgstr "" + +#: wireless/choices.py:469 +msgid "WPA Personal (PSK)" +msgstr "" + +#: wireless/choices.py:470 +msgid "WPA Enterprise" +msgstr "" + +#: wireless/forms/bulk_edit.py:72 wireless/forms/bulk_edit.py:119 +#: wireless/forms/bulk_import.py:68 wireless/forms/bulk_import.py:71 +#: wireless/forms/bulk_import.py:110 wireless/forms/bulk_import.py:113 +#: wireless/forms/filtersets.py:58 wireless/forms/filtersets.py:92 +msgid "Authentication cipher" +msgstr "" + +#: wireless/forms/bulk_edit.py:78 wireless/forms/bulk_edit.py:125 +#: wireless/forms/filtersets.py:63 wireless/forms/filtersets.py:97 +msgid "Pre-shared key" +msgstr "" + +#: wireless/forms/bulk_import.py:52 +msgid "Bridged VLAN" +msgstr "" + +#: wireless/forms/bulk_import.py:89 wireless/tables/wirelesslink.py:27 +msgid "Interface A" +msgstr "" + +#: wireless/forms/bulk_import.py:93 wireless/tables/wirelesslink.py:36 +msgid "Interface B" +msgstr "" + +#: wireless/forms/model_forms.py:158 +msgid "Side B" +msgstr "" + +#: wireless/models.py:30 +msgid "authentication cipher" +msgstr "" + +#: wireless/models.py:38 +msgid "pre-shared key" +msgstr "" + +#: wireless/models.py:68 +msgid "wireless LAN group" +msgstr "" + +#: wireless/models.py:69 +msgid "wireless LAN groups" +msgstr "" + +#: wireless/models.py:115 +msgid "wireless LAN" +msgstr "" + +#: wireless/models.py:143 +msgid "interface A" +msgstr "" + +#: wireless/models.py:150 +msgid "interface B" +msgstr "" + +#: wireless/models.py:198 +msgid "wireless link" +msgstr "" + +#: wireless/models.py:199 +msgid "wireless links" +msgstr "" + +#: wireless/models.py:216 wireless/models.py:222 +#, python-brace-format +msgid "{type} is not a wireless interface." +msgstr "" diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index 1c3233f87..b0a43ef22 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -386,5 +386,5 @@ class ObjectPermissionForm(BootstrapMixin, forms.ModelForm): model.objects.filter(qs_filter_from_constraints(constraints, tokens)).exists() except FieldError as e: raise forms.ValidationError({ - 'constraints': _('Invalid filter for {model}: {e}').format(model=model, e=e) + 'constraints': _('Invalid filter for {model}: {error}').format(model=model, error=e) }) diff --git a/netbox/users/models.py b/netbox/users/models.py index 80fd0dd09..1f8772704 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -169,7 +169,7 @@ class UserConfig(models.Model): elif key in d: err_path = '.'.join(path.split('.')[:i + 1]) raise TypeError( - _("Key '{err_path}' is a leaf node; cannot assign new keys").format(err_path=err_path) + _("Key '{path}' is a leaf node; cannot assign new keys").format(path=err_path) ) else: d = d.setdefault(key, {}) diff --git a/netbox/virtualization/forms/model_forms.py b/netbox/virtualization/forms/model_forms.py index 21dbc895a..73d4ca841 100644 --- a/netbox/virtualization/forms/model_forms.py +++ b/netbox/virtualization/forms/model_forms.py @@ -151,8 +151,12 @@ class ClusterAddDevicesForm(BootstrapMixin, forms.Form): for device in self.cleaned_data.get('devices', []): if device.site != self.cluster.site: raise ValidationError({ - 'devices': _("{} belongs to a different site ({}) than the cluster ({})").format( - device, device.site, self.cluster.site + 'devices': _( + "{device} belongs to a different site ({device_site}) than the cluster ({cluster_site})" + ).format( + device=device, + device_site=device.site, + cluster_site=self.cluster.site ) }) diff --git a/netbox/virtualization/models/clusters.py b/netbox/virtualization/models/clusters.py index 6c8fd0c4b..f8acc4c36 100644 --- a/netbox/virtualization/models/clusters.py +++ b/netbox/virtualization/models/clusters.py @@ -135,10 +135,9 @@ class Cluster(ContactsMixin, PrimaryModel): # If the Cluster is assigned to a Site, verify that all host Devices belong to that Site. if self.pk and self.site: - nonsite_devices = Device.objects.filter(cluster=self).exclude(site=self.site).count() - if nonsite_devices: + if nonsite_devices := Device.objects.filter(cluster=self).exclude(site=self.site).count(): raise ValidationError({ - 'site': _("{} devices are assigned as hosts for this cluster but are not in site {}").format( - nonsite_devices, self.site - ) + 'site': _( + "{count} devices are assigned as hosts for this cluster but are not in site {site}" + ).format(count=nonsite_devices, site=self.site) }) diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py index e8e48eef8..0b114f85f 100644 --- a/netbox/wireless/models.py +++ b/netbox/wireless/models.py @@ -213,14 +213,14 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel): if self.interface_a.type not in WIRELESS_IFACE_TYPES: raise ValidationError({ 'interface_a': _( - "{type_display} is not a wireless interface." - ).format(type_display=self.interface_a.get_type_display()) + "{type} is not a wireless interface." + ).format(type=self.interface_a.get_type_display()) }) if self.interface_b.type not in WIRELESS_IFACE_TYPES: raise ValidationError({ 'interface_a': _( - "{type_display} is not a wireless interface." - ).format(type_display=self.interface_b.get_type_display()) + "{type} is not a wireless interface." + ).format(type=self.interface_b.get_type_display()) }) def save(self, *args, **kwargs): From edc4a35296b80d11851be49fbe3abaae8004e841 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 30 Oct 2023 14:36:56 -0400 Subject: [PATCH 05/10] Initial work on #10244: Protection rules (#14097) --- docs/configuration/data-validation.md | 21 +++++ docs/customization/custom-validation.md | 2 + netbox/extras/forms/model_forms.py | 3 +- netbox/extras/signals.py | 31 +++++-- ...mvalidator.py => test_customvalidation.py} | 90 ++++++++++++++++++- netbox/extras/validators.py | 31 ++++++- netbox/netbox/config/parameters.py | 14 ++- netbox/templates/extras/configrevision.html | 4 + 8 files changed, 183 insertions(+), 13 deletions(-) rename netbox/extras/tests/{test_customvalidator.py => test_customvalidation.py} (64%) diff --git a/docs/configuration/data-validation.md b/docs/configuration/data-validation.md index 9ff71758f..1b8263de3 100644 --- a/docs/configuration/data-validation.md +++ b/docs/configuration/data-validation.md @@ -87,3 +87,24 @@ The following colors are supported: * `gray` * `black` * `white` + +--- + +## PROTECTION_RULES + +!!! tip "Dynamic Configuration Parameter" + +This is a mapping of models to [custom validators](../customization/custom-validation.md) against which an object is evaluated immediately prior to its deletion. If validation fails, the object is not deleted. An example is provided below: + +```python +PROTECTION_RULES = { + "dcim.site": [ + { + "status": { + "eq": "decommissioning" + } + }, + "my_plugin.validators.Validator1", + ] +} +``` diff --git a/docs/customization/custom-validation.md b/docs/customization/custom-validation.md index 30198117f..79aa82bc9 100644 --- a/docs/customization/custom-validation.md +++ b/docs/customization/custom-validation.md @@ -26,6 +26,8 @@ The `CustomValidator` class supports several validation types: * `regex`: Application of a [regular expression](https://en.wikipedia.org/wiki/Regular_expression) * `required`: A value must be specified * `prohibited`: A value must _not_ be specified +* `eq`: A value must be equal to the specified value +* `neq`: A value must _not_ be equal to the specified value The `min` and `max` types should be defined for numeric values, whereas `min_length`, `max_length`, and `regex` are suitable for character strings (text values). The `required` and `prohibited` validators may be used for any field, and should be passed a value of `True`. diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 83a346420..fd2ce8f2d 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -491,7 +491,7 @@ class ConfigRevisionForm(BootstrapMixin, forms.ModelForm, metaclass=ConfigFormMe (_('Security'), ('ALLOWED_URL_SCHEMES',)), (_('Banners'), ('BANNER_LOGIN', 'BANNER_MAINTENANCE', 'BANNER_TOP', 'BANNER_BOTTOM')), (_('Pagination'), ('PAGINATE_COUNT', 'MAX_PAGE_SIZE')), - (_('Validation'), ('CUSTOM_VALIDATORS',)), + (_('Validation'), ('CUSTOM_VALIDATORS', 'PROTECTION_RULES')), (_('User Preferences'), ('DEFAULT_USER_PREFERENCES',)), (_('Miscellaneous'), ( 'MAINTENANCE_MODE', 'GRAPHQL_ENABLED', 'CHANGELOG_RETENTION', 'JOB_RETENTION', 'MAPS_URL', @@ -508,6 +508,7 @@ class ConfigRevisionForm(BootstrapMixin, forms.ModelForm, metaclass=ConfigFormMe 'BANNER_TOP': forms.Textarea(attrs={'class': 'font-monospace'}), 'BANNER_BOTTOM': forms.Textarea(attrs={'class': 'font-monospace'}), 'CUSTOM_VALIDATORS': forms.Textarea(attrs={'class': 'font-monospace'}), + 'PROTECTION_RULES': forms.Textarea(attrs={'class': 'font-monospace'}), 'comment': forms.Textarea(), } diff --git a/netbox/extras/signals.py b/netbox/extras/signals.py index d6550309f..8bdaf523c 100644 --- a/netbox/extras/signals.py +++ b/netbox/extras/signals.py @@ -2,8 +2,10 @@ import importlib import logging from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError from django.db.models.signals import m2m_changed, post_save, pre_delete 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 extras.validators import CustomValidator @@ -178,11 +180,7 @@ m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.content_type # Custom validation # -@receiver(post_clean) -def run_custom_validators(sender, instance, **kwargs): - config = get_config() - model_name = f'{sender._meta.app_label}.{sender._meta.model_name}' - validators = config.CUSTOM_VALIDATORS.get(model_name, []) +def run_validators(instance, validators): for validator in validators: @@ -198,6 +196,29 @@ def run_custom_validators(sender, instance, **kwargs): validator(instance) +@receiver(post_clean) +def run_save_validators(sender, instance, **kwargs): + model_name = f'{sender._meta.app_label}.{sender._meta.model_name}' + validators = get_config().CUSTOM_VALIDATORS.get(model_name, []) + + run_validators(instance, validators) + + +@receiver(pre_delete) +def run_delete_validators(sender, instance, **kwargs): + model_name = f'{sender._meta.app_label}.{sender._meta.model_name}' + validators = get_config().PROTECTION_RULES.get(model_name, []) + + try: + run_validators(instance, validators) + except ValidationError as e: + raise AbortRequest( + _("Deletion is prevented by a protection rule: {message}").format( + message=e + ) + ) + + # # Dynamic configuration # diff --git a/netbox/extras/tests/test_customvalidator.py b/netbox/extras/tests/test_customvalidation.py similarity index 64% rename from netbox/extras/tests/test_customvalidator.py rename to netbox/extras/tests/test_customvalidation.py index 0fe507b67..d74ad599b 100644 --- a/netbox/extras/tests/test_customvalidator.py +++ b/netbox/extras/tests/test_customvalidation.py @@ -1,10 +1,13 @@ from django.conf import settings from django.core.exceptions import ValidationError +from django.db import transaction from django.test import TestCase, override_settings from ipam.models import ASN, RIR +from dcim.choices import SiteStatusChoices from dcim.models import Site from extras.validators import CustomValidator +from utilities.exceptions import AbortRequest class MyValidator(CustomValidator): @@ -14,6 +17,20 @@ class MyValidator(CustomValidator): self.fail("Name must be foo!") +eq_validator = CustomValidator({ + 'asn': { + 'eq': 100 + } +}) + + +neq_validator = CustomValidator({ + 'asn': { + 'neq': 100 + } +}) + + min_validator = CustomValidator({ 'asn': { 'min': 65000 @@ -77,6 +94,18 @@ class CustomValidatorTest(TestCase): validator = settings.CUSTOM_VALIDATORS['ipam.asn'][0] self.assertIsInstance(validator, CustomValidator) + @override_settings(CUSTOM_VALIDATORS={'ipam.asn': [eq_validator]}) + def test_eq(self): + ASN(asn=100, rir=RIR.objects.first()).clean() + with self.assertRaises(ValidationError): + ASN(asn=99, rir=RIR.objects.first()).clean() + + @override_settings(CUSTOM_VALIDATORS={'ipam.asn': [neq_validator]}) + def test_neq(self): + ASN(asn=99, rir=RIR.objects.first()).clean() + with self.assertRaises(ValidationError): + ASN(asn=100, rir=RIR.objects.first()).clean() + @override_settings(CUSTOM_VALIDATORS={'ipam.asn': [min_validator]}) def test_min(self): with self.assertRaises(ValidationError): @@ -147,7 +176,7 @@ class CustomValidatorConfigTest(TestCase): @override_settings( CUSTOM_VALIDATORS={ 'dcim.site': ( - 'extras.tests.test_customvalidator.MyValidator', + 'extras.tests.test_customvalidation.MyValidator', ) } ) @@ -159,3 +188,62 @@ class CustomValidatorConfigTest(TestCase): Site(name='foo', slug='foo').clean() with self.assertRaises(ValidationError): Site(name='bar', slug='bar').clean() + + +class ProtectionRulesConfigTest(TestCase): + + @override_settings( + PROTECTION_RULES={ + 'dcim.site': [ + {'status': {'eq': SiteStatusChoices.STATUS_DECOMMISSIONING}} + ] + } + ) + def test_plain_data(self): + """ + Test custom validator configuration using plain data (as opposed to a CustomValidator + class) + """ + # Create a site with a protected status + site = Site(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE) + site.save() + + # Try to delete it + with self.assertRaises(AbortRequest): + with transaction.atomic(): + site.delete() + + # Change its status to an allowed value + site.status = SiteStatusChoices.STATUS_DECOMMISSIONING + site.save() + + # Deletion should now succeed + site.delete() + + @override_settings( + PROTECTION_RULES={ + 'dcim.site': ( + 'extras.tests.test_customvalidation.MyValidator', + ) + } + ) + def test_dotted_path(self): + """ + Test custom validator configuration using a dotted path (string) reference to a + CustomValidator class. + """ + # Create a site with a protected name + site = Site(name='bar', slug='bar') + site.save() + + # Try to delete it + with self.assertRaises(AbortRequest): + with transaction.atomic(): + site.delete() + + # Change the name to an allowed value + site.name = site.slug = 'foo' + site.save() + + # Deletion should now succeed + site.delete() diff --git a/netbox/extras/validators.py b/netbox/extras/validators.py index 686c9b032..98b4fd88d 100644 --- a/netbox/extras/validators.py +++ b/netbox/extras/validators.py @@ -1,15 +1,38 @@ -from django.core.exceptions import ValidationError from django.core import validators +from django.core.exceptions import ValidationError +from django.utils.translation import gettext_lazy as _ # NOTE: As this module may be imported by configuration.py, we cannot import # anything from NetBox itself. +class IsEqualValidator(validators.BaseValidator): + """ + Employed by CustomValidator to require a specific value. + """ + message = _("Ensure this value is equal to %(limit_value)s.") + code = "is_equal" + + def compare(self, a, b): + return a != b + + +class IsNotEqualValidator(validators.BaseValidator): + """ + Employed by CustomValidator to exclude a specific value. + """ + message = _("Ensure this value does not equal %(limit_value)s.") + code = "is_not_equal" + + def compare(self, a, b): + return a == b + + class IsEmptyValidator: """ Employed by CustomValidator to enforce required fields. """ - message = "This field must be empty." + message = _("This field must be empty.") code = 'is_empty' def __init__(self, enforce=True): @@ -24,7 +47,7 @@ class IsNotEmptyValidator: """ Employed by CustomValidator to enforce prohibited fields. """ - message = "This field must not be empty." + message = _("This field must not be empty.") code = 'not_empty' def __init__(self, enforce=True): @@ -50,6 +73,8 @@ class CustomValidator: :param validation_rules: A dictionary mapping object attributes to validation rules """ VALIDATORS = { + 'eq': IsEqualValidator, + 'neq': IsNotEqualValidator, 'min': validators.MinValueValidator, 'max': validators.MaxValueValidator, 'min_length': validators.MinLengthValidator, diff --git a/netbox/netbox/config/parameters.py b/netbox/netbox/config/parameters.py index 31c4f693a..0cdf8a8d2 100644 --- a/netbox/netbox/config/parameters.py +++ b/netbox/netbox/config/parameters.py @@ -152,9 +152,17 @@ PARAMS = ( description=_("Custom validation rules (JSON)"), field=forms.JSONField, field_kwargs={ - 'widget': forms.Textarea( - attrs={'class': 'vLargeTextField'} - ), + 'widget': forms.Textarea(), + }, + ), + ConfigParam( + name='PROTECTION_RULES', + label=_('Protection rules'), + default={}, + description=_("Deletion protection rules (JSON)"), + field=forms.JSONField, + field_kwargs={ + 'widget': forms.Textarea(), }, ), diff --git a/netbox/templates/extras/configrevision.html b/netbox/templates/extras/configrevision.html index 4f2abf30b..a880865c3 100644 --- a/netbox/templates/extras/configrevision.html +++ b/netbox/templates/extras/configrevision.html @@ -151,6 +151,10 @@ {% trans "Custom validators" %} {{ object.data.CUSTOM_VALIDATORS|placeholder }} + + {% trans "Protection rules" %} + {{ object.data.PROTECTION_RULES|placeholder }} + From 7323668dd0a5a1452a7e94c2f43c9bce60383755 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 31 Oct 2023 08:34:57 -0400 Subject: [PATCH 06/10] Closes #13334: Record error message on failed jobs (#14106) --- netbox/core/api/serializers.py | 2 +- netbox/core/jobs.py | 2 +- .../migrations/0006_job_add_error_field.py | 18 ++++++++++++++++++ netbox/core/models/jobs.py | 9 ++++++++- netbox/core/tables/jobs.py | 2 +- netbox/extras/management/commands/runscript.py | 2 +- netbox/extras/reports.py | 6 +++--- netbox/extras/scripts.py | 2 +- netbox/templates/core/job.html | 6 ++++++ 9 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 netbox/core/migrations/0006_job_add_error_field.py diff --git a/netbox/core/api/serializers.py b/netbox/core/api/serializers.py index 0d743d952..4ae426df5 100644 --- a/netbox/core/api/serializers.py +++ b/netbox/core/api/serializers.py @@ -69,5 +69,5 @@ class JobSerializer(BaseModelSerializer): model = Job fields = [ 'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval', - 'started', 'completed', 'user', 'data', 'job_id', + 'started', 'completed', 'user', 'data', 'error', 'job_id', ] diff --git a/netbox/core/jobs.py b/netbox/core/jobs.py index d25981920..32b546b20 100644 --- a/netbox/core/jobs.py +++ b/netbox/core/jobs.py @@ -25,7 +25,7 @@ def sync_datasource(job, *args, **kwargs): job.terminate() except Exception as e: - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) DataSource.objects.filter(pk=datasource.pk).update(status=DataSourceStatusChoices.FAILED) if type(e) in (SyncError, JobTimeoutException): logging.error(e) diff --git a/netbox/core/migrations/0006_job_add_error_field.py b/netbox/core/migrations/0006_job_add_error_field.py new file mode 100644 index 000000000..2927db4c4 --- /dev/null +++ b/netbox/core/migrations/0006_job_add_error_field.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.6 on 2023-10-23 20:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_job_created_auto_now'), + ] + + operations = [ + migrations.AddField( + model_name='job', + name='error', + field=models.TextField(blank=True, editable=False), + ), + ] diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index 61b0e64fa..4e9a93bfb 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -92,6 +92,11 @@ class Job(models.Model): null=True, blank=True ) + error = models.TextField( + verbose_name=_('error'), + editable=False, + blank=True + ) job_id = models.UUIDField( verbose_name=_('job ID'), unique=True @@ -158,7 +163,7 @@ class Job(models.Model): # Handle webhooks self.trigger_webhooks(event=EVENT_JOB_START) - def terminate(self, status=JobStatusChoices.STATUS_COMPLETED): + def terminate(self, status=JobStatusChoices.STATUS_COMPLETED, error=None): """ Mark the job as completed, optionally specifying a particular termination status. """ @@ -168,6 +173,8 @@ class Job(models.Model): # Mark the job as completed self.status = status + if error: + self.error = error self.completed = timezone.now() self.save() diff --git a/netbox/core/tables/jobs.py b/netbox/core/tables/jobs.py index 32ca67f7f..3388aee19 100644 --- a/netbox/core/tables/jobs.py +++ b/netbox/core/tables/jobs.py @@ -47,7 +47,7 @@ class JobTable(NetBoxTable): model = Job fields = ( 'pk', 'id', 'object_type', 'object', 'name', 'status', 'created', 'scheduled', 'interval', 'started', - 'completed', 'user', 'job_id', + 'completed', 'user', 'error', 'job_id', ) default_columns = ( 'pk', 'id', 'object_type', 'object', 'name', 'status', 'created', 'started', 'completed', 'user', diff --git a/netbox/extras/management/commands/runscript.py b/netbox/extras/management/commands/runscript.py index d9a9f41ae..3cf70281c 100644 --- a/netbox/extras/management/commands/runscript.py +++ b/netbox/extras/management/commands/runscript.py @@ -59,7 +59,7 @@ class Command(BaseCommand): logger.error(f"Exception raised during script execution: {e}") clear_webhooks.send(request) job.data = ScriptOutputSerializer(script).data - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) logger.info(f"Script completed in {job.duration}") diff --git a/netbox/extras/reports.py b/netbox/extras/reports.py index cc279a49a..c8a13fe15 100644 --- a/netbox/extras/reports.py +++ b/netbox/extras/reports.py @@ -40,8 +40,8 @@ def run_report(job, *args, **kwargs): try: report.run(job) - except Exception: - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + except Exception as e: + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) logging.error(f"Error during execution of report {job.name}") finally: # Schedule the next job if an interval has been set @@ -230,7 +230,7 @@ class Report(object): stacktrace = traceback.format_exc() self.log_failure(None, f"An exception occurred: {type(e).__name__}: {e}
{stacktrace}
") logger.error(f"Exception raised during report execution: {e}") - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) # Perform any post-run tasks self.post_run() diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index e93326ddc..df75200e6 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -519,7 +519,7 @@ def run_script(data, request, job, commit=True, **kwargs): logger.error(f"Exception raised during script execution: {e}") script.log_info("Database changes have been reverted due to error.") job.data = ScriptOutputSerializer(script).data - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) clear_webhooks.send(request) logger.info(f"Script completed in {job.duration}") diff --git a/netbox/templates/core/job.html b/netbox/templates/core/job.html index 1fe3862cd..deb651739 100644 --- a/netbox/templates/core/job.html +++ b/netbox/templates/core/job.html @@ -35,6 +35,12 @@ {% trans "Status" %} {% badge object.get_status_display object.get_status_color %} + {% if object.error %} + + {% trans "Error" %} + {{ object.error }} + + {% endif %} {% trans "Created By" %} {{ object.user|placeholder }} From 77208bf5f3af6e7f2500baf676a127f07fea319a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 31 Oct 2023 08:41:24 -0400 Subject: [PATCH 07/10] Fix migrations --- ...{0006_job_add_error_field.py => 0007_job_add_error_field.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename netbox/core/migrations/{0006_job_add_error_field.py => 0007_job_add_error_field.py} (85%) diff --git a/netbox/core/migrations/0006_job_add_error_field.py b/netbox/core/migrations/0007_job_add_error_field.py similarity index 85% rename from netbox/core/migrations/0006_job_add_error_field.py rename to netbox/core/migrations/0007_job_add_error_field.py index 2927db4c4..e2e173bfd 100644 --- a/netbox/core/migrations/0006_job_add_error_field.py +++ b/netbox/core/migrations/0007_job_add_error_field.py @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('core', '0005_job_created_auto_now'), + ('core', '0006_datasource_type_remove_choices'), ] operations = [ From c2d1988cb3f74f8a4f7c6005d32caadc69164610 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Nov 2023 11:56:14 -0400 Subject: [PATCH 08/10] Closes #14035: Order global search results of equivalent weight by value (#14140) --- .../migrations/0099_cachedvalue_ordering.py | 17 +++++++++++++++++ netbox/extras/models/search.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 netbox/extras/migrations/0099_cachedvalue_ordering.py diff --git a/netbox/extras/migrations/0099_cachedvalue_ordering.py b/netbox/extras/migrations/0099_cachedvalue_ordering.py new file mode 100644 index 000000000..242ffd983 --- /dev/null +++ b/netbox/extras/migrations/0099_cachedvalue_ordering.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.6 on 2023-10-30 14:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0098_webhook_custom_field_data_webhook_tags'), + ] + + operations = [ + migrations.AlterModelOptions( + name='cachedvalue', + options={'ordering': ('weight', 'object_type', 'value', 'object_id')}, + ), + ] diff --git a/netbox/extras/models/search.py b/netbox/extras/models/search.py index debe4c648..39ff80215 100644 --- a/netbox/extras/models/search.py +++ b/netbox/extras/models/search.py @@ -50,7 +50,7 @@ class CachedValue(models.Model): ) class Meta: - ordering = ('weight', 'object_type', 'object_id') + ordering = ('weight', 'object_type', 'value', 'object_id') verbose_name = _('cached value') verbose_name_plural = _('cached values') From 944008d4753194276908b54dc7f3e0952ec7a4f6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Nov 2023 13:47:14 -0400 Subject: [PATCH 09/10] Closes #12135: Prevent the deletion of interfaces with children (#14091) * Closes #12135: Prevent the deletion of interfaces with children * Change PROTECT to RESTRICT * Extend handle_protectederror() to also handle RestrictedError * Fix string translation * Update migrations * Support bulk removal of parent interfaces via UI if all children are included * Add support for the bulk deletion of restricted objects via REST API --- docs/models/dcim/interface.md | 3 ++ docs/models/virtualization/vminterface.md | 3 ++ netbox/dcim/api/views.py | 5 ++++ .../0183_protect_child_interfaces.py | 19 ++++++++++++ netbox/dcim/models/device_components.py | 2 +- netbox/dcim/tests/test_api.py | 27 +++++++++++++++++ netbox/dcim/tests/test_views.py | 30 +++++++++++++++++++ netbox/dcim/views.py | 5 ++-- netbox/netbox/api/viewsets/__init__.py | 9 ++++-- netbox/netbox/api/viewsets/mixins.py | 10 +++++-- netbox/netbox/views/generic/bulk_views.py | 17 ++++++----- netbox/netbox/views/generic/object_views.py | 6 ++-- netbox/utilities/error_handlers.py | 20 +++++++++---- netbox/virtualization/api/views.py | 5 ++++ .../0037_protect_child_interfaces.py | 19 ++++++++++++ netbox/virtualization/tests/test_api.py | 26 ++++++++++++++++ netbox/virtualization/tests/test_views.py | 29 ++++++++++++++++++ netbox/virtualization/views.py | 5 ++-- 18 files changed, 214 insertions(+), 26 deletions(-) create mode 100644 netbox/dcim/migrations/0183_protect_child_interfaces.py create mode 100644 netbox/virtualization/migrations/0037_protect_child_interfaces.py diff --git a/docs/models/dcim/interface.md b/docs/models/dcim/interface.md index 42b570964..3667dabd5 100644 --- a/docs/models/dcim/interface.md +++ b/docs/models/dcim/interface.md @@ -77,6 +77,9 @@ If selected, this component will be treated as if a cable has been connected. Virtual interfaces can be bound to a physical parent interface. This is helpful for modeling virtual interfaces which employ encapsulation on a physical interface, such as an 802.1Q VLAN-tagged subinterface. +!!! note + An interface with one or more child interfaces assigned cannot be deleted until all its child interfaces have been deleted or reassigned. + ### Bridged Interface Interfaces can be bridged to other interfaces on a device in two manners: symmetric or grouped. diff --git a/docs/models/virtualization/vminterface.md b/docs/models/virtualization/vminterface.md index 264fb95ba..d923bdd5d 100644 --- a/docs/models/virtualization/vminterface.md +++ b/docs/models/virtualization/vminterface.md @@ -16,6 +16,9 @@ The interface's name. Must be unique to the assigned VM. Identifies the parent interface of a subinterface (e.g. used to employ encapsulation). +!!! note + An interface with one or more child interfaces assigned cannot be deleted until all its child interfaces have been deleted or reassigned. + ### Bridged Interface An interface on the same VM with which this interface is bridged. diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index 80a991736..a3e532f0b 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -24,6 +24,7 @@ from netbox.api.viewsets import NetBoxModelViewSet, MPTTLockedMixin from netbox.api.viewsets.mixins import SequentialBulkCreatesMixin from netbox.constants import NESTED_SERIALIZER_PREFIX from utilities.api import get_serializer_for_model +from utilities.query_functions import CollateAsChar from utilities.utils import count_related from virtualization.models import VirtualMachine from . import serializers @@ -505,6 +506,10 @@ class InterfaceViewSet(PathEndpointMixin, NetBoxModelViewSet): filterset_class = filtersets.InterfaceFilterSet brief_prefetch_fields = ['device'] + def get_bulk_destroy_queryset(self): + # Ensure child interfaces are deleted prior to their parents + return self.get_queryset().order_by('device', 'parent', CollateAsChar('_name')) + class FrontPortViewSet(PassThroughPortMixin, NetBoxModelViewSet): queryset = FrontPort.objects.prefetch_related( diff --git a/netbox/dcim/migrations/0183_protect_child_interfaces.py b/netbox/dcim/migrations/0183_protect_child_interfaces.py new file mode 100644 index 000000000..ca695f4bd --- /dev/null +++ b/netbox/dcim/migrations/0183_protect_child_interfaces.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.6 on 2023-10-20 11:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0182_devicetype_exclude_from_utilization'), + ] + + operations = [ + migrations.AlterField( + model_name='interface', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='child_interfaces', to='dcim.interface'), + ), + ] diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 639f8aadb..94568459e 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -537,7 +537,7 @@ class BaseInterface(models.Model): ) parent = models.ForeignKey( to='self', - on_delete=models.SET_NULL, + on_delete=models.RESTRICT, related_name='child_interfaces', null=True, blank=True, diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 1ce362963..d3211a75f 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -1607,6 +1607,33 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase }, ] + def test_bulk_delete_child_interfaces(self): + interface1 = Interface.objects.get(name='Interface 1') + device = interface1.device + self.add_permissions('dcim.delete_interface') + + # Create a child interface + child = Interface.objects.create( + device=device, + name='Interface 1A', + type=InterfaceTypeChoices.TYPE_VIRTUAL, + parent=interface1 + ) + self.assertEqual(device.interfaces.count(), 4) + + # Attempt to delete only the parent interface + url = self._get_detail_url(interface1) + self.client.delete(url, **self.header) + self.assertEqual(device.interfaces.count(), 4) # Parent was not deleted + + # Attempt to bulk delete parent & child together + data = [ + {"id": interface1.pk}, + {"id": child.pk}, + ] + self.client.delete(self._get_list_url(), data, format='json', **self.header) + self.assertEqual(device.interfaces.count(), 2) # Child & parent were both deleted + class FrontPortTest(APIViewTestCases.APIViewTestCase): model = FrontPort diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index a6981451f..88e0d44f2 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -2531,6 +2531,36 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): response = self.client.get(reverse('dcim:interface_trace', kwargs={'pk': interface1.pk})) self.assertHttpStatus(response, 200) + def test_bulk_delete_child_interfaces(self): + interface1 = Interface.objects.get(name='Interface 1') + device = interface1.device + self.add_permissions('dcim.delete_interface') + + # Create a child interface + child = Interface.objects.create( + device=device, + name='Interface 1A', + type=InterfaceTypeChoices.TYPE_VIRTUAL, + parent=interface1 + ) + self.assertEqual(device.interfaces.count(), 6) + + # Attempt to delete only the parent interface + data = { + 'confirm': True, + } + self.client.post(self._get_url('delete', interface1), data) + self.assertEqual(device.interfaces.count(), 6) # Parent was not deleted + + # Attempt to bulk delete parent & child together + data = { + 'pk': [interface1.pk, child.pk], + 'confirm': True, + '_confirm': True, # Form button + } + self.client.post(self._get_url('bulk_delete'), data) + self.assertEqual(device.interfaces.count(), 4) # Child & parent were both deleted + class FrontPortTestCase(ViewTestCases.DeviceComponentViewTestCase): model = FrontPort diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 0f5768173..be0d6bcbb 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1,5 +1,4 @@ import traceback -from collections import defaultdict from django.contrib import messages from django.contrib.contenttypes.models import ContentType @@ -26,6 +25,7 @@ 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 +from utilities.query_functions import CollateAsChar from utilities.utils import count_related from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view from virtualization.models import VirtualMachine @@ -2562,7 +2562,8 @@ class InterfaceBulkDisconnectView(BulkDisconnectView): class InterfaceBulkDeleteView(generic.BulkDeleteView): - queryset = Interface.objects.all() + # Ensure child interfaces are deleted prior to their parents + queryset = Interface.objects.order_by('device', 'parent', CollateAsChar('_name')) filterset = filtersets.InterfaceFilterSet table = tables.InterfaceTable diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index c6794bb61..522bcf77b 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -2,7 +2,7 @@ import logging from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.db import transaction -from django.db.models import ProtectedError +from django.db.models import ProtectedError, RestrictedError from django_pglocks import advisory_lock from netbox.constants import ADVISORY_LOCK_KEYS from rest_framework import mixins as drf_mixins @@ -91,8 +91,11 @@ class NetBoxModelViewSet( try: return super().dispatch(request, *args, **kwargs) - except ProtectedError as e: - protected_objects = list(e.protected_objects) + except (ProtectedError, RestrictedError) as e: + if type(e) is ProtectedError: + protected_objects = list(e.protected_objects) + else: + protected_objects = list(e.restricted_objects) msg = f'Unable to delete object. {len(protected_objects)} dependent objects were found: ' msg += ', '.join([f'{obj} ({obj.pk})' for obj in protected_objects]) logger.warning(msg) diff --git a/netbox/netbox/api/viewsets/mixins.py b/netbox/netbox/api/viewsets/mixins.py index fde486fe9..7b6c00843 100644 --- a/netbox/netbox/api/viewsets/mixins.py +++ b/netbox/netbox/api/viewsets/mixins.py @@ -137,11 +137,14 @@ class BulkUpdateModelMixin: } ] """ + def get_bulk_update_queryset(self): + return self.get_queryset() + def bulk_update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) serializer = BulkOperationSerializer(data=request.data, many=True) serializer.is_valid(raise_exception=True) - qs = self.get_queryset().filter( + qs = self.get_bulk_update_queryset().filter( pk__in=[o['id'] for o in serializer.data] ) @@ -184,10 +187,13 @@ class BulkDestroyModelMixin: {"id": 456} ] """ + def get_bulk_destroy_queryset(self): + return self.get_queryset() + def bulk_destroy(self, request, *args, **kwargs): serializer = BulkOperationSerializer(data=request.data, many=True) serializer.is_valid(raise_exception=True) - qs = self.get_queryset().filter( + qs = self.get_bulk_destroy_queryset().filter( pk__in=[o['id'] for o in serializer.data] ) diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 676e3f5af..fbe3aa2ba 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.fields import GenericRel from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError from django.db import transaction, IntegrityError -from django.db.models import ManyToManyField, ProtectedError +from django.db.models import ManyToManyField, ProtectedError, RestrictedError from django.db.models.fields.reverse_related import ManyToManyRel from django.forms import HiddenInput, ModelMultipleChoiceField, MultipleHiddenInput from django.http import HttpResponse @@ -798,14 +798,15 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView): queryset = self.queryset.filter(pk__in=pk_list) deleted_count = queryset.count() try: - for obj in queryset: - # Take a snapshot of change-logged models - if hasattr(obj, 'snapshot'): - obj.snapshot() - obj.delete() + with transaction.atomic(): + for obj in queryset: + # Take a snapshot of change-logged models + if hasattr(obj, 'snapshot'): + obj.snapshot() + obj.delete() - except ProtectedError as e: - logger.info("Caught ProtectedError while attempting to delete objects") + except (ProtectedError, RestrictedError) as e: + logger.info(f"Caught {type(e)} while attempting to delete objects") handle_protectederror(queryset, request, e) return redirect(self.get_return_url(request)) diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 99d8ff540..7c737aaf0 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -3,7 +3,7 @@ from copy import deepcopy from django.contrib import messages from django.db import transaction -from django.db.models import ProtectedError +from django.db.models import ProtectedError, RestrictedError from django.shortcuts import redirect, render from django.urls import reverse from django.utils.html import escape @@ -374,8 +374,8 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): try: obj.delete() - except ProtectedError as e: - logger.info("Caught ProtectedError while attempting to delete object") + except (ProtectedError, RestrictedError) as e: + logger.info(f"Caught {type(e)} while attempting to delete objects") handle_protectederror([obj], request, e) return redirect(obj.get_absolute_url()) diff --git a/netbox/utilities/error_handlers.py b/netbox/utilities/error_handlers.py index 1d3bdbafd..9af12ac2e 100644 --- a/netbox/utilities/error_handlers.py +++ b/netbox/utilities/error_handlers.py @@ -1,16 +1,26 @@ from django.contrib import messages +from django.db.models import ProtectedError, RestrictedError from django.utils.html import escape from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ def handle_protectederror(obj_list, request, e): """ - Generate a user-friendly error message in response to a ProtectedError exception. + Generate a user-friendly error message in response to a ProtectedError or RestrictedError exception. """ - protected_objects = list(e.protected_objects) - protected_count = len(protected_objects) if len(protected_objects) <= 50 else 'More than 50' - err_message = f"Unable to delete {', '.join(str(obj) for obj in obj_list)}. " \ - f"{protected_count} dependent objects were found: " + if type(e) is ProtectedError: + protected_objects = list(e.protected_objects) + elif type(e) is RestrictedError: + protected_objects = list(e.restricted_objects) + else: + raise e + + # Formulate the error message + err_message = _("Unable to delete {objects}. {count} dependent objects were found: ").format( + objects=', '.join(str(obj) for obj in obj_list), + count=len(protected_objects) if len(protected_objects) <= 50 else _('More than 50') + ) # Append dependent objects to error message dependent_objects = [] diff --git a/netbox/virtualization/api/views.py b/netbox/virtualization/api/views.py index 5b9cf4117..04e8f2167 100644 --- a/netbox/virtualization/api/views.py +++ b/netbox/virtualization/api/views.py @@ -3,6 +3,7 @@ from rest_framework.routers import APIRootView from dcim.models import Device from extras.api.mixins import ConfigContextQuerySetMixin from netbox.api.viewsets import NetBoxModelViewSet +from utilities.query_functions import CollateAsChar from utilities.utils import count_related from virtualization import filtersets from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface @@ -87,3 +88,7 @@ class VMInterfaceViewSet(NetBoxModelViewSet): serializer_class = serializers.VMInterfaceSerializer filterset_class = filtersets.VMInterfaceFilterSet brief_prefetch_fields = ['virtual_machine'] + + def get_bulk_destroy_queryset(self): + # Ensure child interfaces are deleted prior to their parents + return self.get_queryset().order_by('virtual_machine', 'parent', CollateAsChar('_name')) diff --git a/netbox/virtualization/migrations/0037_protect_child_interfaces.py b/netbox/virtualization/migrations/0037_protect_child_interfaces.py new file mode 100644 index 000000000..ab6cf0cb3 --- /dev/null +++ b/netbox/virtualization/migrations/0037_protect_child_interfaces.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.6 on 2023-10-20 11:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('virtualization', '0036_virtualmachine_config_template'), + ] + + operations = [ + migrations.AlterField( + model_name='vminterface', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='child_interfaces', to='virtualization.vminterface'), + ), + ] diff --git a/netbox/virtualization/tests/test_api.py b/netbox/virtualization/tests/test_api.py index b2ae68860..3fb46fbb9 100644 --- a/netbox/virtualization/tests/test_api.py +++ b/netbox/virtualization/tests/test_api.py @@ -293,3 +293,29 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase): 'vrf': vrfs[2].pk, }, ] + + def test_bulk_delete_child_interfaces(self): + interface1 = VMInterface.objects.get(name='Interface 1') + virtual_machine = interface1.virtual_machine + self.add_permissions('virtualization.delete_vminterface') + + # Create a child interface + child = VMInterface.objects.create( + virtual_machine=virtual_machine, + name='Interface 1A', + parent=interface1 + ) + self.assertEqual(virtual_machine.interfaces.count(), 4) + + # Attempt to delete only the parent interface + url = self._get_detail_url(interface1) + self.client.delete(url, **self.header) + self.assertEqual(virtual_machine.interfaces.count(), 4) # Parent was not deleted + + # Attempt to bulk delete parent & child together + data = [ + {"id": interface1.pk}, + {"id": child.pk}, + ] + self.client.delete(self._get_list_url(), data, format='json', **self.header) + self.assertEqual(virtual_machine.interfaces.count(), 2) # Child & parent were both deleted diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index a5d831d7e..f47c386e9 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -374,3 +374,32 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase): 'untagged_vlan': vlans[0].pk, 'tagged_vlans': [v.pk for v in vlans[1:4]], } + + def test_bulk_delete_child_interfaces(self): + interface1 = VMInterface.objects.get(name='Interface 1') + virtual_machine = interface1.virtual_machine + self.add_permissions('virtualization.delete_vminterface') + + # Create a child interface + child = VMInterface.objects.create( + virtual_machine=virtual_machine, + name='Interface 1A', + parent=interface1 + ) + self.assertEqual(virtual_machine.interfaces.count(), 4) + + # Attempt to delete only the parent interface + data = { + 'confirm': True, + } + self.client.post(self._get_url('delete', interface1), data) + self.assertEqual(virtual_machine.interfaces.count(), 4) # Parent was not deleted + + # Attempt to bulk delete parent & child together + data = { + 'pk': [interface1.pk, child.pk], + 'confirm': True, + '_confirm': True, # Form button + } + self.client.post(self._get_url('bulk_delete'), data) + self.assertEqual(virtual_machine.interfaces.count(), 2) # Child & parent were both deleted diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 798d1fc4d..e8782243f 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -1,5 +1,4 @@ import traceback -from collections import defaultdict from django.contrib import messages from django.db import transaction @@ -19,6 +18,7 @@ from ipam.tables import InterfaceVLANTable from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.views import generic from tenancy.views import ObjectContactsView +from utilities.query_functions import CollateAsChar from utilities.utils import count_related from utilities.views import ViewTab, register_model_view from . import filtersets, forms, tables @@ -550,7 +550,8 @@ class VMInterfaceBulkRenameView(generic.BulkRenameView): class VMInterfaceBulkDeleteView(generic.BulkDeleteView): - queryset = VMInterface.objects.all() + # Ensure child interfaces are deleted prior to their parents + queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name')) filterset = filtersets.VMInterfaceFilterSet table = tables.VMInterfaceTable From f6338abf14ae1e43e213b13df0293931cb2543c0 Mon Sep 17 00:00:00 2001 From: "Jamie (Bear) Murphy" <1613241+ITJamie@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:13:45 +0000 Subject: [PATCH 10/10] Closes #13690: List all objects to be deleted (#14089) * show objects that would be deleted by cascade * some items were not showing (eg ips on devices) * dont include the item being deleted in the list of related items * Revert "dont include the item being deleted in the list of related items" This reverts commit 298a7860b20c2fd90e887c66c4f196460097e71e. * cleanup - migrate code to use collector directly instead of the NestedObjects wrapper from admin.utils - adjust object names and text output * requested adjustments * remove comma from end of list * linting * refactor, add accordion * migrate to defaultdict, use title for capitalisation of accordian titles * Misc cleanup --------- Co-authored-by: Jeremy Stretch --- netbox/netbox/views/generic/object_views.py | 28 ++++++++++++++++- netbox/templates/htmx/delete_form.html | 34 +++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 7c737aaf0..99508c9e3 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -1,9 +1,11 @@ import logging +from collections import defaultdict from copy import deepcopy from django.contrib import messages -from django.db import transaction +from django.db import router, transaction from django.db.models import ProtectedError, RestrictedError +from django.db.models.deletion import Collector from django.shortcuts import redirect, render from django.urls import reverse from django.utils.html import escape @@ -320,6 +322,27 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'delete') + def _get_dependent_objects(self, obj): + """ + Returns a dictionary mapping of dependent objects (organized by model) which will be deleted as a result of + deleting the requested object. + + Args: + obj: The object to return dependent objects for + """ + using = router.db_for_write(obj._meta.model) + collector = Collector(using=using) + collector.collect([obj]) + + # Compile a mapping of models to instances + dependent_objects = defaultdict(list) + for model, instance in collector.instances_with_model(): + # Omit the root object + if instance != obj: + dependent_objects[model].append(instance) + + return dict(dependent_objects) + # # Request handlers # @@ -333,6 +356,7 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): """ obj = self.get_object(**kwargs) form = ConfirmationForm(initial=request.GET) + dependent_objects = self._get_dependent_objects(obj) # If this is an HTMX request, return only the rendered deletion form as modal content if is_htmx(request): @@ -343,6 +367,7 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): 'object_type': self.queryset.model._meta.verbose_name, 'form': form, 'form_url': form_url, + 'dependent_objects': dependent_objects, **self.get_extra_context(request, obj), }) @@ -350,6 +375,7 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): 'object': obj, 'form': form, 'return_url': self.get_return_url(request, obj), + 'dependent_objects': dependent_objects, **self.get_extra_context(request, obj), }) diff --git a/netbox/templates/htmx/delete_form.html b/netbox/templates/htmx/delete_form.html index 15f08ebfd..80aec2c82 100644 --- a/netbox/templates/htmx/delete_form.html +++ b/netbox/templates/htmx/delete_form.html @@ -12,6 +12,40 @@ Are you sure you want to delete {{ object_type }} {{ object }}? {% endblocktrans %}

+ {% if dependent_objects %} +

+ {% trans "The following objects will be deleted as a result of this action." %} +

+
+ {% for model, instances in dependent_objects.items %} +
+

+ +

+
+
+
+ {% for instance in instances %} + {% with url=instance.get_absolute_url %} + {{ instance }} + {% endwith %} + {% endfor %} +
+
+
+
+ {% endfor %} +
+ {% endif %} {% render_form form %}