mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-08 21:02:18 -06:00
Compare commits
10 Commits
88be56fbf7
...
1598ca9108
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1598ca9108 | ||
|
|
21f4036782 | ||
|
|
ce3738572c | ||
|
|
cbb979934e | ||
|
|
642d83a4c6 | ||
|
|
a06c12c6b8 | ||
|
|
59afa0b41d | ||
|
|
14b246cb8a | ||
|
|
9ae53fc232 | ||
|
|
c111c08315 |
@@ -20,4 +20,4 @@ class ManufacturerSerializer(NetBoxModelSerializer):
|
|||||||
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tags', 'custom_fields',
|
||||||
'created', 'last_updated', 'devicetype_count', 'inventoryitem_count', 'platform_count',
|
'created', 'last_updated', 'devicetype_count', 'inventoryitem_count', 'platform_count',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'devicetype_count')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description')
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from circuits.models import Circuit
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
@@ -1414,19 +1415,52 @@ class MACAddressImportForm(NetBoxModelImportForm):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class CableImportForm(NetBoxModelImportForm):
|
class CableImportForm(NetBoxModelImportForm):
|
||||||
|
"""
|
||||||
|
CSV bulk import form for cables.
|
||||||
|
|
||||||
|
Supports dynamic parent model resolution - terminations are identified by their parent
|
||||||
|
object (device, circuit, or power panel) and termination name.
|
||||||
|
|
||||||
|
The parent field resolves to different models based on the termination type
|
||||||
|
See CABLE_PARENT_MAPPING for supported termination types.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Map cable termination content types to their parent model and lookup field.
|
||||||
|
#
|
||||||
|
# This mapping enables dynamic parent model resolution during cable CSV imports.
|
||||||
|
# Each entry maps a termination type to a tuple of (parent_content_type, accessor):
|
||||||
|
#
|
||||||
|
# Format: 'app.model': ('parent_app.ParentModel', 'accessor')
|
||||||
|
#
|
||||||
|
CABLE_PARENT_MAPPING = {
|
||||||
|
'dcim.interface': ('dcim.Device', 'name'),
|
||||||
|
'dcim.consoleport': ('dcim.Device', 'name'),
|
||||||
|
'dcim.consoleserverport': ('dcim.Device', 'name'),
|
||||||
|
'dcim.powerport': ('dcim.Device', 'name'),
|
||||||
|
'dcim.poweroutlet': ('dcim.Device', 'name'),
|
||||||
|
'dcim.frontport': ('dcim.Device', 'name'),
|
||||||
|
'dcim.rearport': ('dcim.Device', 'name'),
|
||||||
|
'circuits.circuittermination': ('circuits.Circuit', 'cid'),
|
||||||
|
'dcim.powerfeed': ('dcim.PowerPanel', 'name'),
|
||||||
|
}
|
||||||
|
# Map parent model name to (parent_field_name, termination_name_field, value_transform)
|
||||||
|
TERMINATION_FIELDS = {
|
||||||
|
'Circuit': ('circuit', 'term_side', str.upper),
|
||||||
|
'Device': ('device', 'name', None),
|
||||||
|
'PowerPanel': ('power_panel', 'name', None),
|
||||||
|
}
|
||||||
|
|
||||||
# Termination A
|
# Termination A
|
||||||
side_a_site = CSVModelChoiceField(
|
side_a_site = CSVModelChoiceField(
|
||||||
label=_('Side A site'),
|
label=_('Side A site'),
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text=_('Site of parent device A (if any)'),
|
help_text=_('Site of parent A (if any)')
|
||||||
)
|
)
|
||||||
side_a_device = CSVModelChoiceField(
|
side_a_parent = forms.CharField(
|
||||||
label=_('Side A device'),
|
label=_('Side A parent'),
|
||||||
queryset=Device.objects.all(),
|
help_text=_('Device name, Circuit CID, or Power Panel name')
|
||||||
to_field_name='name',
|
|
||||||
help_text=_('Device name')
|
|
||||||
)
|
)
|
||||||
side_a_type = CSVContentTypeField(
|
side_a_type = CSVContentTypeField(
|
||||||
label=_('Side A type'),
|
label=_('Side A type'),
|
||||||
@@ -1445,13 +1479,11 @@ class CableImportForm(NetBoxModelImportForm):
|
|||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text=_('Site of parent device B (if any)'),
|
help_text=_('Site of parent B (if any)')
|
||||||
)
|
)
|
||||||
side_b_device = CSVModelChoiceField(
|
side_b_parent = forms.CharField(
|
||||||
label=_('Side B device'),
|
label=_('Side B parent'),
|
||||||
queryset=Device.objects.all(),
|
help_text=_('Device name, Circuit CID, or Power Panel name')
|
||||||
to_field_name='name',
|
|
||||||
help_text=_('Device name')
|
|
||||||
)
|
)
|
||||||
side_b_type = CSVContentTypeField(
|
side_b_type = CSVContentTypeField(
|
||||||
label=_('Side B type'),
|
label=_('Side B type'),
|
||||||
@@ -1500,7 +1532,7 @@ class CableImportForm(NetBoxModelImportForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Cable
|
model = Cable
|
||||||
fields = [
|
fields = [
|
||||||
'side_a_site', 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_device', 'side_b_type',
|
'side_a_site', 'side_a_parent', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_parent', 'side_b_type',
|
||||||
'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description',
|
'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description',
|
||||||
'comments', 'tags',
|
'comments', 'tags',
|
||||||
]
|
]
|
||||||
@@ -1508,21 +1540,6 @@ class CableImportForm(NetBoxModelImportForm):
|
|||||||
def __init__(self, data=None, *args, **kwargs):
|
def __init__(self, data=None, *args, **kwargs):
|
||||||
super().__init__(data, *args, **kwargs)
|
super().__init__(data, *args, **kwargs)
|
||||||
|
|
||||||
if data:
|
|
||||||
# Limit choices for side_a_device to the assigned side_a_site
|
|
||||||
if side_a_site := data.get('side_a_site'):
|
|
||||||
side_a_device_params = {f'site__{self.fields["side_a_site"].to_field_name}': side_a_site}
|
|
||||||
self.fields['side_a_device'].queryset = self.fields['side_a_device'].queryset.filter(
|
|
||||||
**side_a_device_params
|
|
||||||
)
|
|
||||||
|
|
||||||
# Limit choices for side_b_device to the assigned side_b_site
|
|
||||||
if side_b_site := data.get('side_b_site'):
|
|
||||||
side_b_device_params = {f'site__{self.fields["side_b_site"].to_field_name}': side_b_site}
|
|
||||||
self.fields['side_b_device'].queryset = self.fields['side_b_device'].queryset.filter(
|
|
||||||
**side_b_device_params
|
|
||||||
)
|
|
||||||
|
|
||||||
def _clean_side(self, side):
|
def _clean_side(self, side):
|
||||||
"""
|
"""
|
||||||
Derive a Cable's A/B termination objects.
|
Derive a Cable's A/B termination objects.
|
||||||
@@ -1531,31 +1548,118 @@ class CableImportForm(NetBoxModelImportForm):
|
|||||||
"""
|
"""
|
||||||
assert side in 'ab', f"Invalid side designation: {side}"
|
assert side in 'ab', f"Invalid side designation: {side}"
|
||||||
|
|
||||||
device = self.cleaned_data.get(f'side_{side}_device')
|
|
||||||
content_type = self.cleaned_data.get(f'side_{side}_type')
|
content_type = self.cleaned_data.get(f'side_{side}_type')
|
||||||
|
site = self.cleaned_data.get(f'side_{side}_site')
|
||||||
|
parent_value = self.cleaned_data.get(f'side_{side}_parent')
|
||||||
name = self.cleaned_data.get(f'side_{side}_name')
|
name = self.cleaned_data.get(f'side_{side}_name')
|
||||||
if not device or not content_type or not name:
|
|
||||||
|
if not parent_value or not content_type or not name: # pragma: no cover
|
||||||
return None
|
return None
|
||||||
|
|
||||||
model = content_type.model_class()
|
# Get the parent model mapping from the submitted content_type
|
||||||
|
parent_map = self.CABLE_PARENT_MAPPING.get(f'{content_type.app_label}.{content_type.model}')
|
||||||
|
# This should never happen
|
||||||
|
assert parent_map, (
|
||||||
|
'Unknown cable termination content type parent mapping: '
|
||||||
|
f'{content_type.app_label}.{content_type.model}'
|
||||||
|
)
|
||||||
|
|
||||||
|
parent_content_type, parent_accessor = parent_map
|
||||||
|
parent_app_label, parent_model_name = parent_content_type.split('.')
|
||||||
|
|
||||||
|
# Get the parent model class
|
||||||
try:
|
try:
|
||||||
if device.virtual_chassis and device.virtual_chassis.master == device and \
|
parent_ct = ContentType.objects.get(app_label=parent_app_label.lower(), model=parent_model_name.lower())
|
||||||
model.objects.filter(device=device, name=name).count() == 0:
|
parent_model: Device | PowerPanel | Circuit = parent_ct.model_class()
|
||||||
termination_object = model.objects.get(device__in=device.virtual_chassis.members.all(), name=name)
|
except ContentType.DoesNotExist: # pragma: no cover
|
||||||
else:
|
# This should never happen
|
||||||
termination_object = model.objects.get(device=device, name=name)
|
raise AssertionError(f'Unknown cable termination parent content type: {parent_content_type}')
|
||||||
if termination_object.cable is not None and termination_object.cable != self.instance:
|
|
||||||
raise forms.ValidationError(
|
# Build query for parent lookup
|
||||||
_("Side {side_upper}: {device} {termination_object} is already connected").format(
|
parent_query = {parent_accessor: parent_value}
|
||||||
side_upper=side.upper(), device=device, termination_object=termination_object
|
# Add site to query if provided
|
||||||
)
|
if site:
|
||||||
)
|
parent_query['site'] = site
|
||||||
except ObjectDoesNotExist:
|
|
||||||
|
# Look up the parent object
|
||||||
|
try:
|
||||||
|
parent_object = parent_model.objects.get(**parent_query)
|
||||||
|
except parent_model.DoesNotExist:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_("{side_upper} side termination not found: {device} {name}").format(
|
_('Side {side_upper}: {model_name} not found: {value}').format(
|
||||||
side_upper=side.upper(), device=device, name=name
|
side_upper=side.upper(), model_name=parent_model.__name__, value=parent_value
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
except parent_model.MultipleObjectsReturned:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Side {side_upper}: Multiple {model_name} objects found: {value}').format(
|
||||||
|
side_upper=side.upper(), model_name=parent_model.__name__, value=parent_value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the termination model class
|
||||||
|
termination_model = content_type.model_class()
|
||||||
|
|
||||||
|
# Build the query to find the termination object
|
||||||
|
field_mapping = self.TERMINATION_FIELDS.get(parent_model.__name__)
|
||||||
|
if not field_mapping: # pragma: no cover
|
||||||
|
return None
|
||||||
|
|
||||||
|
parent_field, name_field, value_transform = field_mapping
|
||||||
|
query = {parent_field: parent_object}
|
||||||
|
|
||||||
|
if value_transform:
|
||||||
|
name = value_transform(name)
|
||||||
|
|
||||||
|
if name:
|
||||||
|
query[name_field] = name
|
||||||
|
|
||||||
|
# Add site to query if provided (for site-scoped parents)
|
||||||
|
if site and parent_field in ('device', 'power_panel'):
|
||||||
|
query[f'{parent_field}__site'] = site
|
||||||
|
|
||||||
|
# Look up the termination object
|
||||||
|
try:
|
||||||
|
# Handle virtual chassis for device-based terminations
|
||||||
|
if (parent_field == 'device' and
|
||||||
|
parent_object.virtual_chassis and
|
||||||
|
parent_object.virtual_chassis.master == parent_object and
|
||||||
|
termination_model.objects.filter(**query).count() == 0):
|
||||||
|
query[f'{parent_field}__in'] = parent_object.virtual_chassis.members.all()
|
||||||
|
query.pop(parent_field, None)
|
||||||
|
termination_object = termination_model.objects.get(**query)
|
||||||
|
else:
|
||||||
|
termination_object = termination_model.objects.get(**query)
|
||||||
|
|
||||||
|
# Check if already connected to a cable
|
||||||
|
if termination_object.cable is not None and termination_object.cable != self.instance:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Side {side_upper}: {parent} {termination} is already connected').format(
|
||||||
|
side_upper=side.upper(), parent=parent_object, termination=termination_object
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Circuit terminations can also be connected to provider networks
|
||||||
|
if (name_field == 'term_side' and
|
||||||
|
hasattr(termination_object, '_provider_network') and
|
||||||
|
termination_object._provider_network is not None):
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Side {side_upper}: {parent} {termination} is already connected to a provider network').format(
|
||||||
|
side_upper=side.upper(), parent=parent_object, termination=termination_object
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except termination_model.DoesNotExist:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Side {side_upper}: {model_name} not found: {parent} {name}').format(
|
||||||
|
side_upper=side.upper(),
|
||||||
|
model_name=termination_model.__name__,
|
||||||
|
parent=parent_object, name=name or '',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
except termination_model.MultipleObjectsReturned: # pragma: no cover
|
||||||
|
# This should never happen
|
||||||
|
raise AssertionError('Multiple termination objects returned for query: {query}'.format(query=query))
|
||||||
|
|
||||||
setattr(self.instance, f'{side}_terminations', [termination_object])
|
setattr(self.instance, f'{side}_terminations', [termination_object])
|
||||||
return termination_object
|
return termination_object
|
||||||
|
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
|
|
||||||
class ManufacturerTest(APIViewTestCases.APIViewTestCase):
|
class ManufacturerTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
brief_fields = ['description', 'devicetype_count', 'display', 'id', 'name', 'slug', 'url']
|
brief_fields = ['description', 'display', 'id', 'name', 'slug', 'url']
|
||||||
create_data = [
|
create_data = [
|
||||||
{
|
{
|
||||||
'name': 'Manufacturer 4',
|
'name': 'Manufacturer 4',
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider, ProviderNetwork
|
||||||
from dcim.choices import (
|
from dcim.choices import (
|
||||||
DeviceFaceChoices, DeviceStatusChoices, InterfaceModeChoices, InterfaceTypeChoices, PortTypeChoices,
|
CableTypeChoices, DeviceFaceChoices, DeviceStatusChoices, InterfaceModeChoices, InterfaceTypeChoices,
|
||||||
PowerOutletStatusChoices,
|
PortTypeChoices, PowerOutletStatusChoices,
|
||||||
)
|
)
|
||||||
from dcim.forms import *
|
from dcim.forms import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
@@ -411,3 +412,204 @@ class InterfaceTestCase(TestCase):
|
|||||||
self.assertNotIn('untagged_vlan', form.cleaned_data.keys())
|
self.assertNotIn('untagged_vlan', form.cleaned_data.keys())
|
||||||
self.assertNotIn('tagged_vlans', form.cleaned_data.keys())
|
self.assertNotIn('tagged_vlans', form.cleaned_data.keys())
|
||||||
self.assertNotIn('qinq_svlan', form.cleaned_data.keys())
|
self.assertNotIn('qinq_svlan', form.cleaned_data.keys())
|
||||||
|
|
||||||
|
|
||||||
|
class CableImportFormTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
Test cases for CableImportForm error handling and edge cases.
|
||||||
|
|
||||||
|
Note: Happy path scenarios (successful cable creation) are covered by
|
||||||
|
dcim.tests.test_views.CableTestCase which tests the bulk import view.
|
||||||
|
These tests focus on validation errors and edge cases not covered by the view tests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
# Create sites
|
||||||
|
cls.site_a = Site.objects.create(name='Site A', slug='site-a')
|
||||||
|
cls.site_b = Site.objects.create(name='Site B', slug='site-b')
|
||||||
|
|
||||||
|
# Create manufacturer and device type
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
|
device_type = DeviceType.objects.create(
|
||||||
|
manufacturer=manufacturer,
|
||||||
|
model='Device Type 1',
|
||||||
|
slug='device-type-1',
|
||||||
|
)
|
||||||
|
role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1', color='ff0000')
|
||||||
|
|
||||||
|
# Create devices
|
||||||
|
cls.device_a1 = Device.objects.create(
|
||||||
|
name='Device-A1',
|
||||||
|
device_type=device_type,
|
||||||
|
role=role,
|
||||||
|
site=cls.site_a,
|
||||||
|
)
|
||||||
|
cls.device_a2 = Device.objects.create(
|
||||||
|
name='Device-A2',
|
||||||
|
device_type=device_type,
|
||||||
|
role=role,
|
||||||
|
site=cls.site_a,
|
||||||
|
)
|
||||||
|
# Device with same name in different site
|
||||||
|
cls.device_b_duplicate = Device.objects.create(
|
||||||
|
name='Device-A1', # Same name as device_a1
|
||||||
|
device_type=device_type,
|
||||||
|
role=role,
|
||||||
|
site=cls.site_b,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create interfaces
|
||||||
|
cls.interface_a1_eth0 = Interface.objects.create(
|
||||||
|
device=cls.device_a1,
|
||||||
|
name='eth0',
|
||||||
|
type=InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||||
|
)
|
||||||
|
cls.interface_a2_eth0 = Interface.objects.create(
|
||||||
|
device=cls.device_a2,
|
||||||
|
name='eth0',
|
||||||
|
type=InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create circuit for testing circuit not found error
|
||||||
|
provider = Provider.objects.create(name='Provider 1', slug='provider-1')
|
||||||
|
circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
|
||||||
|
cls.circuit = Circuit.objects.create(
|
||||||
|
provider=provider,
|
||||||
|
type=circuit_type,
|
||||||
|
cid='CIRCUIT-001',
|
||||||
|
)
|
||||||
|
cls.circuit_term_a = CircuitTermination.objects.create(
|
||||||
|
circuit=cls.circuit,
|
||||||
|
term_side='A',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create provider network for testing provider network validation
|
||||||
|
cls.provider_network = ProviderNetwork.objects.create(
|
||||||
|
provider=provider,
|
||||||
|
name='Provider Network 1',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_device_not_found(self):
|
||||||
|
"""Test error when parent device is not found."""
|
||||||
|
form = CableImportForm(data={
|
||||||
|
'side_a_site': 'Site A',
|
||||||
|
'side_a_parent': 'NonexistentDevice',
|
||||||
|
'side_a_type': 'dcim.interface',
|
||||||
|
'side_a_name': 'eth0',
|
||||||
|
'side_b_site': 'Site A',
|
||||||
|
'side_b_parent': 'Device-A2',
|
||||||
|
'side_b_type': 'dcim.interface',
|
||||||
|
'side_b_name': 'eth0',
|
||||||
|
'type': CableTypeChoices.TYPE_CAT6,
|
||||||
|
'status': 'connected',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertIn('Side A: Device not found: NonexistentDevice', str(form.errors))
|
||||||
|
|
||||||
|
def test_circuit_not_found(self):
|
||||||
|
"""Test error when circuit is not found."""
|
||||||
|
form = CableImportForm(data={
|
||||||
|
'side_a_site': None,
|
||||||
|
'side_a_parent': 'NONEXISTENT-CID',
|
||||||
|
'side_a_type': 'circuits.circuittermination',
|
||||||
|
'side_a_name': 'A',
|
||||||
|
'side_b_site': 'Site A',
|
||||||
|
'side_b_parent': 'Device-A1',
|
||||||
|
'side_b_type': 'dcim.interface',
|
||||||
|
'side_b_name': 'eth0',
|
||||||
|
'type': CableTypeChoices.TYPE_MMF_OM4,
|
||||||
|
'status': 'connected',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertIn('Side A: Circuit not found: NONEXISTENT-CID', str(form.errors))
|
||||||
|
|
||||||
|
def test_termination_not_found(self):
|
||||||
|
"""Test error when termination is not found on parent."""
|
||||||
|
form = CableImportForm(data={
|
||||||
|
'side_a_site': 'Site A',
|
||||||
|
'side_a_parent': 'Device-A1',
|
||||||
|
'side_a_type': 'dcim.interface',
|
||||||
|
'side_a_name': 'eth999', # Nonexistent interface
|
||||||
|
'side_b_site': 'Site A',
|
||||||
|
'side_b_parent': 'Device-A2',
|
||||||
|
'side_b_type': 'dcim.interface',
|
||||||
|
'side_b_name': 'eth0',
|
||||||
|
'type': CableTypeChoices.TYPE_CAT6,
|
||||||
|
'status': 'connected',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertIn('Side A: Interface not found', str(form.errors))
|
||||||
|
|
||||||
|
def test_termination_already_cabled(self):
|
||||||
|
"""Test error when termination is already connected to a cable."""
|
||||||
|
# Create an existing cable
|
||||||
|
existing_cable = Cable.objects.create(type=CableTypeChoices.TYPE_CAT6, status='connected')
|
||||||
|
self.interface_a1_eth0.cable = existing_cable
|
||||||
|
self.interface_a1_eth0.save()
|
||||||
|
|
||||||
|
form = CableImportForm(data={
|
||||||
|
'side_a_site': 'Site A',
|
||||||
|
'side_a_parent': 'Device-A1',
|
||||||
|
'side_a_type': 'dcim.interface',
|
||||||
|
'side_a_name': 'eth0',
|
||||||
|
'side_b_site': 'Site A',
|
||||||
|
'side_b_parent': 'Device-A2',
|
||||||
|
'side_b_type': 'dcim.interface',
|
||||||
|
'side_b_name': 'eth0',
|
||||||
|
'type': CableTypeChoices.TYPE_CAT6,
|
||||||
|
'status': 'connected',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertIn('already connected', str(form.errors))
|
||||||
|
|
||||||
|
def test_circuit_termination_with_provider_network(self):
|
||||||
|
"""Test error when circuit termination is already connected to a provider network."""
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
# Connect circuit termination to provider network
|
||||||
|
circuit_term = CircuitTermination.objects.get(pk=self.circuit_term_a.pk)
|
||||||
|
pn_ct = ContentType.objects.get_for_model(ProviderNetwork)
|
||||||
|
circuit_term.termination_type = pn_ct
|
||||||
|
circuit_term.termination_id = self.provider_network.pk
|
||||||
|
circuit_term.save()
|
||||||
|
|
||||||
|
try:
|
||||||
|
form = CableImportForm(data={
|
||||||
|
'side_a_site': None,
|
||||||
|
'side_a_parent': 'CIRCUIT-001',
|
||||||
|
'side_a_type': 'circuits.circuittermination',
|
||||||
|
'side_a_name': 'A',
|
||||||
|
'side_b_site': 'Site A',
|
||||||
|
'side_b_parent': 'Device-A1',
|
||||||
|
'side_b_type': 'dcim.interface',
|
||||||
|
'side_b_name': 'eth0',
|
||||||
|
'type': CableTypeChoices.TYPE_MMF_OM4,
|
||||||
|
'status': 'connected',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertIn('already connected to a provider network', str(form.errors))
|
||||||
|
finally:
|
||||||
|
# Clean up: remove provider network connection
|
||||||
|
circuit_term.termination_type = None
|
||||||
|
circuit_term.termination_id = None
|
||||||
|
circuit_term.save()
|
||||||
|
|
||||||
|
def test_multiple_parents_without_site(self):
|
||||||
|
"""Test error when multiple parent objects are found without site scoping."""
|
||||||
|
# Device-A1 exists in both site_a and site_b
|
||||||
|
# Try to find device without specifying site
|
||||||
|
form = CableImportForm(data={
|
||||||
|
'side_a_site': '', # Empty site - should cause multiple matches
|
||||||
|
'side_a_parent': 'Device-A1',
|
||||||
|
'side_a_type': 'dcim.interface',
|
||||||
|
'side_a_name': 'eth0',
|
||||||
|
'side_b_site': 'Site A',
|
||||||
|
'side_b_parent': 'Device-A2',
|
||||||
|
'side_b_type': 'dcim.interface',
|
||||||
|
'side_b_name': 'eth0',
|
||||||
|
'type': CableTypeChoices.TYPE_CAT6,
|
||||||
|
'status': 'connected',
|
||||||
|
})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
self.assertIn('Multiple Device objects found', str(form.errors))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from core.models import ObjectType
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
|
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||||
from ipam.models import ASN, RIR, VLAN, VRF
|
from ipam.models import ASN, RIR, VLAN, VRF
|
||||||
from netbox.choices import CSVDelimiterChoices, ImportFormatChoices, WeightUnitChoices
|
from netbox.choices import CSVDelimiterChoices, ImportFormatChoices, WeightUnitChoices
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
@@ -3495,7 +3496,7 @@ class CableTestCase(
|
|||||||
Interface(device=devices[4], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
Interface(device=devices[4], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||||
Interface(device=devices[4], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
Interface(device=devices[4], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||||
|
|
||||||
# Device 1, Site 2
|
# Device 5, Site 2
|
||||||
Interface(device=devices[5], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
Interface(device=devices[5], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||||
Interface(device=devices[5], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
Interface(device=devices[5], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||||
Interface(device=devices[5], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
Interface(device=devices[5], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||||
@@ -3507,6 +3508,22 @@ class CableTestCase(
|
|||||||
)
|
)
|
||||||
Interface.objects.bulk_create(interfaces)
|
Interface.objects.bulk_create(interfaces)
|
||||||
|
|
||||||
|
ConsolePort.objects.create(device=devices[0], name='Console 1')
|
||||||
|
ConsoleServerPort.objects.create(device=devices[1], name='Console Server 1')
|
||||||
|
|
||||||
|
power_panel = PowerPanel.objects.create(site=sites[0], name='Power Panel 1')
|
||||||
|
PowerFeed.objects.create(power_panel=power_panel, name='Feed 1')
|
||||||
|
PowerPort.objects.create(device=devices[0], name='PSU1')
|
||||||
|
|
||||||
|
provider = Provider.objects.create(name='Provider 1', slug='provider-1')
|
||||||
|
circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
|
||||||
|
circuit = Circuit.objects.create(provider=provider, type=circuit_type, cid='CIRCUIT-001')
|
||||||
|
circuit_terminations = (
|
||||||
|
CircuitTermination(circuit=circuit, term_side='A'),
|
||||||
|
CircuitTermination(circuit=circuit, term_side='Z'),
|
||||||
|
)
|
||||||
|
CircuitTermination.objects.bulk_create(circuit_terminations)
|
||||||
|
|
||||||
cable1 = Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[3]], type=CableTypeChoices.TYPE_CAT6)
|
cable1 = Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[3]], type=CableTypeChoices.TYPE_CAT6)
|
||||||
cable1.save()
|
cable1.save()
|
||||||
cable2 = Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[4]], type=CableTypeChoices.TYPE_CAT6)
|
cable2 = Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[4]], type=CableTypeChoices.TYPE_CAT6)
|
||||||
@@ -3532,7 +3549,7 @@ class CableTestCase(
|
|||||||
|
|
||||||
cls.csv_data = {
|
cls.csv_data = {
|
||||||
'default': (
|
'default': (
|
||||||
"side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name",
|
"side_a_parent,side_a_type,side_a_name,side_b_parent,side_b_type,side_b_name",
|
||||||
"Device 4,dcim.interface,Interface 1,Device 5,dcim.interface,Interface 1",
|
"Device 4,dcim.interface,Interface 1,Device 5,dcim.interface,Interface 1",
|
||||||
"Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2",
|
"Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2",
|
||||||
"Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3",
|
"Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3",
|
||||||
@@ -3545,12 +3562,28 @@ class CableTestCase(
|
|||||||
'site-filtering': (
|
'site-filtering': (
|
||||||
# Ensure that CSV bulk import supports assigning terminations from parent devices
|
# Ensure that CSV bulk import supports assigning terminations from parent devices
|
||||||
# that share the same device name, provided those devices belong to different sites.
|
# that share the same device name, provided those devices belong to different sites.
|
||||||
"side_a_site,side_a_device,side_a_type,side_a_name,side_b_site,side_b_device,side_b_type,side_b_name",
|
"side_a_site,side_a_parent,side_a_type,side_a_name,side_b_site,side_b_parent,side_b_type,side_b_name",
|
||||||
"Site 1,Device 3,dcim.interface,Interface 1,Site 2,Device 1,dcim.interface,Interface 1",
|
"Site 1,Device 3,dcim.interface,Interface 1,Site 2,Device 1,dcim.interface,Interface 1",
|
||||||
"Site 1,Device 3,dcim.interface,Interface 2,Site 2,Device 1,dcim.interface,Interface 2",
|
"Site 1,Device 3,dcim.interface,Interface 2,Site 2,Device 1,dcim.interface,Interface 2",
|
||||||
"Site 1,Device 3,dcim.interface,Interface 3,Site 2,Device 1,dcim.interface,Interface 3",
|
"Site 1,Device 3,dcim.interface,Interface 3,Site 2,Device 1,dcim.interface,Interface 3",
|
||||||
"Site 1,Device 1,dcim.interface,Device 2 Interface,Site 2,Device 1,dcim.interface,Interface 4",
|
"Site 1,Device 1,dcim.interface,Device 2 Interface,Site 2,Device 1,dcim.interface,Interface 4",
|
||||||
"Site 1,Device 1,dcim.interface,Device 3 Interface,Site 2,Device 1,dcim.interface,Interface 5",
|
"Site 1,Device 1,dcim.interface,Device 3 Interface,Site 2,Device 1,dcim.interface,Interface 5",
|
||||||
|
),
|
||||||
|
'circuits': (
|
||||||
|
# Test circuit termination to interface cables
|
||||||
|
"side_a_parent,side_a_type,side_a_name,side_b_site,side_b_parent,side_b_type,side_b_name",
|
||||||
|
"CIRCUIT-001,circuits.circuittermination,A,Site 1,Device 4,dcim.interface,Interface 2",
|
||||||
|
"CIRCUIT-001,circuits.circuittermination,z,Site 2,Device 5,dcim.interface,Interface 2",
|
||||||
|
),
|
||||||
|
'power': (
|
||||||
|
# Test power feed to power port cables
|
||||||
|
"side_a_site,side_a_parent,side_a_type,side_a_name,side_b_site,side_b_parent,side_b_type,side_b_name",
|
||||||
|
"Site 1,Power Panel 1,dcim.powerfeed,Feed 1,Site 1,Device 1,dcim.powerport,PSU1",
|
||||||
|
),
|
||||||
|
'console': (
|
||||||
|
# Test console port to console server port cables
|
||||||
|
"side_a_site,side_a_parent,side_a_type,side_a_name,side_b_site,side_b_parent,side_b_type,side_b_name",
|
||||||
|
"Site 1,Device 1,dcim.consoleport,Console 1,Site 1,Device 2,dcim.consoleserverport,Console Server 1",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -230,10 +230,6 @@ class PrefixImportForm(ScopedImportForm, NetBoxModelImportForm):
|
|||||||
query |= Q(**{
|
query |= Q(**{
|
||||||
f"site__{self.fields['vlan_site'].to_field_name}": vlan_site
|
f"site__{self.fields['vlan_site'].to_field_name}": vlan_site
|
||||||
})
|
})
|
||||||
# Don't Forget to include VLANs without a site in the filter
|
|
||||||
query |= Q(**{
|
|
||||||
f"site__{self.fields['vlan_site'].to_field_name}__isnull": True
|
|
||||||
})
|
|
||||||
|
|
||||||
if vlan_group:
|
if vlan_group:
|
||||||
query &= Q(**{
|
query &= Q(**{
|
||||||
|
|||||||
@@ -564,6 +564,82 @@ vlan: 102
|
|||||||
self.assertEqual(prefix.vlan.vid, 102)
|
self.assertEqual(prefix.vlan.vid, 102)
|
||||||
self.assertEqual(prefix.scope, site)
|
self.assertEqual(prefix.scope, site)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_prefix_import_with_vlan_site_multiple_vlans_same_vid(self):
|
||||||
|
"""
|
||||||
|
Test import when multiple VLANs exist with the same vid but different sites.
|
||||||
|
Ref: #20560
|
||||||
|
"""
|
||||||
|
site1 = Site.objects.get(name='Site 1')
|
||||||
|
site2 = Site.objects.get(name='Site 2')
|
||||||
|
|
||||||
|
# Create VLANs with the same vid but different sites
|
||||||
|
vlan1 = VLAN.objects.create(vid=1, name='VLAN1-Site1', site=site1)
|
||||||
|
VLAN.objects.create(vid=1, name='VLAN1-Site2', site=site2) # Create ambiguity
|
||||||
|
|
||||||
|
# Import prefix with vlan_site specified
|
||||||
|
IMPORT_DATA = f"""
|
||||||
|
prefix: 10.11.0.0/22
|
||||||
|
status: active
|
||||||
|
scope_type: dcim.site
|
||||||
|
scope_id: {site1.pk}
|
||||||
|
vlan_site: {site1.name}
|
||||||
|
vlan: 1
|
||||||
|
description: LOC02-MGMT
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Add all required permissions to the test user
|
||||||
|
self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'data': IMPORT_DATA,
|
||||||
|
'format': 'yaml'
|
||||||
|
}
|
||||||
|
response = self.client.post(reverse('ipam:prefix_bulk_import'), data=form_data, follow=True)
|
||||||
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
# Verify the prefix was created with the correct VLAN
|
||||||
|
prefix = Prefix.objects.get(prefix='10.11.0.0/22')
|
||||||
|
self.assertEqual(prefix.vlan, vlan1)
|
||||||
|
|
||||||
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||||
|
def test_prefix_import_with_vlan_site_and_global_vlan(self):
|
||||||
|
"""
|
||||||
|
Test import when a global VLAN (no site) and site-specific VLAN exist with same vid.
|
||||||
|
When vlan_site is specified, should prefer the site-specific VLAN.
|
||||||
|
Ref: #20560
|
||||||
|
"""
|
||||||
|
site1 = Site.objects.get(name='Site 1')
|
||||||
|
|
||||||
|
# Create a global VLAN (no site) and a site-specific VLAN with the same vid
|
||||||
|
VLAN.objects.create(vid=10, name='VLAN10-Global', site=None) # Create ambiguity
|
||||||
|
vlan_site = VLAN.objects.create(vid=10, name='VLAN10-Site1', site=site1)
|
||||||
|
|
||||||
|
# Import prefix with vlan_site specified
|
||||||
|
IMPORT_DATA = f"""
|
||||||
|
prefix: 10.12.0.0/22
|
||||||
|
status: active
|
||||||
|
scope_type: dcim.site
|
||||||
|
scope_id: {site1.pk}
|
||||||
|
vlan_site: {site1.name}
|
||||||
|
vlan: 10
|
||||||
|
description: Test Site-Specific VLAN
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Add all required permissions to the test user
|
||||||
|
self.add_permissions('ipam.view_prefix', 'ipam.add_prefix')
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'data': IMPORT_DATA,
|
||||||
|
'format': 'yaml'
|
||||||
|
}
|
||||||
|
response = self.client.post(reverse('ipam:prefix_bulk_import'), data=form_data, follow=True)
|
||||||
|
self.assertHttpStatus(response, 200)
|
||||||
|
|
||||||
|
# Verify the prefix was created with the site-specific VLAN (not the global one)
|
||||||
|
prefix = Prefix.objects.get(prefix='10.12.0.0/22')
|
||||||
|
self.assertEqual(prefix.vlan, vlan_site)
|
||||||
|
|
||||||
|
|
||||||
class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
class IPRangeTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = IPRange
|
model = IPRange
|
||||||
|
|||||||
2
netbox/project-static/dist/netbox.css
vendored
2
netbox/project-static/dist/netbox.css
vendored
File diff suppressed because one or more lines are too long
@@ -36,7 +36,6 @@ form.object-edit {
|
|||||||
// Make optgroup labels sticky when scrolling through select elements
|
// Make optgroup labels sticky when scrolling through select elements
|
||||||
select[multiple] {
|
select[multiple] {
|
||||||
optgroup {
|
optgroup {
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
background-color: var(--bs-body-bg);
|
background-color: var(--bs-body-bg);
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-12-10 05:02+0000\n"
|
"POT-Creation-Date: 2025-12-12 05:02+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -231,7 +231,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:125
|
#: netbox/dcim/tables/power.py:93 netbox/dcim/tables/racks.py:125
|
||||||
#: netbox/dcim/tables/racks.py:215 netbox/dcim/tables/sites.py:151
|
#: netbox/dcim/tables/racks.py:215 netbox/dcim/tables/sites.py:151
|
||||||
#: netbox/extras/filtersets.py:662 netbox/ipam/forms/bulk_edit.py:479
|
#: netbox/extras/filtersets.py:662 netbox/ipam/forms/bulk_edit.py:479
|
||||||
#: netbox/ipam/forms/bulk_import.py:489 netbox/ipam/forms/filtersets.py:161
|
#: netbox/ipam/forms/bulk_import.py:485 netbox/ipam/forms/filtersets.py:161
|
||||||
#: netbox/ipam/forms/filtersets.py:236 netbox/ipam/forms/filtersets.py:457
|
#: netbox/ipam/forms/filtersets.py:236 netbox/ipam/forms/filtersets.py:457
|
||||||
#: netbox/ipam/forms/filtersets.py:552 netbox/ipam/forms/model_forms.py:673
|
#: netbox/ipam/forms/filtersets.py:552 netbox/ipam/forms/model_forms.py:673
|
||||||
#: netbox/ipam/tables/vlans.py:90 netbox/ipam/tables/vlans.py:200
|
#: netbox/ipam/tables/vlans.py:90 netbox/ipam/tables/vlans.py:200
|
||||||
@@ -784,8 +784,8 @@ msgstr ""
|
|||||||
#: netbox/dcim/tables/sites.py:96 netbox/dcim/tables/sites.py:155
|
#: netbox/dcim/tables/sites.py:96 netbox/dcim/tables/sites.py:155
|
||||||
#: netbox/ipam/forms/bulk_edit.py:240 netbox/ipam/forms/bulk_edit.py:290
|
#: netbox/ipam/forms/bulk_edit.py:240 netbox/ipam/forms/bulk_edit.py:290
|
||||||
#: netbox/ipam/forms/bulk_edit.py:343 netbox/ipam/forms/bulk_edit.py:501
|
#: netbox/ipam/forms/bulk_edit.py:343 netbox/ipam/forms/bulk_edit.py:501
|
||||||
#: netbox/ipam/forms/bulk_import.py:195 netbox/ipam/forms/bulk_import.py:263
|
#: netbox/ipam/forms/bulk_import.py:195 netbox/ipam/forms/bulk_import.py:259
|
||||||
#: netbox/ipam/forms/bulk_import.py:299 netbox/ipam/forms/bulk_import.py:510
|
#: netbox/ipam/forms/bulk_import.py:295 netbox/ipam/forms/bulk_import.py:506
|
||||||
#: netbox/ipam/forms/filtersets.py:219 netbox/ipam/forms/filtersets.py:297
|
#: netbox/ipam/forms/filtersets.py:219 netbox/ipam/forms/filtersets.py:297
|
||||||
#: netbox/ipam/forms/filtersets.py:379 netbox/ipam/forms/filtersets.py:564
|
#: netbox/ipam/forms/filtersets.py:379 netbox/ipam/forms/filtersets.py:564
|
||||||
#: netbox/ipam/forms/model_forms.py:512 netbox/ipam/tables/ip.py:184
|
#: netbox/ipam/forms/model_forms.py:512 netbox/ipam/tables/ip.py:184
|
||||||
@@ -866,8 +866,8 @@ msgstr ""
|
|||||||
#: netbox/ipam/forms/bulk_import.py:41 netbox/ipam/forms/bulk_import.py:70
|
#: netbox/ipam/forms/bulk_import.py:41 netbox/ipam/forms/bulk_import.py:70
|
||||||
#: netbox/ipam/forms/bulk_import.py:98 netbox/ipam/forms/bulk_import.py:118
|
#: netbox/ipam/forms/bulk_import.py:98 netbox/ipam/forms/bulk_import.py:118
|
||||||
#: netbox/ipam/forms/bulk_import.py:138 netbox/ipam/forms/bulk_import.py:167
|
#: netbox/ipam/forms/bulk_import.py:138 netbox/ipam/forms/bulk_import.py:167
|
||||||
#: netbox/ipam/forms/bulk_import.py:256 netbox/ipam/forms/bulk_import.py:292
|
#: netbox/ipam/forms/bulk_import.py:252 netbox/ipam/forms/bulk_import.py:288
|
||||||
#: netbox/ipam/forms/bulk_import.py:472 netbox/ipam/forms/bulk_import.py:503
|
#: netbox/ipam/forms/bulk_import.py:468 netbox/ipam/forms/bulk_import.py:499
|
||||||
#: netbox/ipam/forms/filtersets.py:50 netbox/ipam/forms/filtersets.py:70
|
#: netbox/ipam/forms/filtersets.py:50 netbox/ipam/forms/filtersets.py:70
|
||||||
#: netbox/ipam/forms/filtersets.py:102 netbox/ipam/forms/filtersets.py:123
|
#: netbox/ipam/forms/filtersets.py:102 netbox/ipam/forms/filtersets.py:123
|
||||||
#: netbox/ipam/forms/filtersets.py:146 netbox/ipam/forms/filtersets.py:182
|
#: netbox/ipam/forms/filtersets.py:146 netbox/ipam/forms/filtersets.py:182
|
||||||
@@ -1106,8 +1106,8 @@ msgstr ""
|
|||||||
#: netbox/extras/filtersets.py:689 netbox/ipam/forms/bulk_edit.py:245
|
#: netbox/extras/filtersets.py:689 netbox/ipam/forms/bulk_edit.py:245
|
||||||
#: netbox/ipam/forms/bulk_edit.py:295 netbox/ipam/forms/bulk_edit.py:348
|
#: netbox/ipam/forms/bulk_edit.py:295 netbox/ipam/forms/bulk_edit.py:348
|
||||||
#: netbox/ipam/forms/bulk_edit.py:506 netbox/ipam/forms/bulk_import.py:200
|
#: netbox/ipam/forms/bulk_edit.py:506 netbox/ipam/forms/bulk_import.py:200
|
||||||
#: netbox/ipam/forms/bulk_import.py:268 netbox/ipam/forms/bulk_import.py:304
|
#: netbox/ipam/forms/bulk_import.py:264 netbox/ipam/forms/bulk_import.py:300
|
||||||
#: netbox/ipam/forms/bulk_import.py:515 netbox/ipam/forms/filtersets.py:247
|
#: netbox/ipam/forms/bulk_import.py:511 netbox/ipam/forms/filtersets.py:247
|
||||||
#: netbox/ipam/forms/filtersets.py:305 netbox/ipam/forms/filtersets.py:384
|
#: netbox/ipam/forms/filtersets.py:305 netbox/ipam/forms/filtersets.py:384
|
||||||
#: netbox/ipam/forms/filtersets.py:572 netbox/ipam/forms/model_forms.py:195
|
#: netbox/ipam/forms/filtersets.py:572 netbox/ipam/forms/model_forms.py:195
|
||||||
#: netbox/ipam/forms/model_forms.py:221 netbox/ipam/forms/model_forms.py:260
|
#: netbox/ipam/forms/model_forms.py:221 netbox/ipam/forms/model_forms.py:260
|
||||||
@@ -1160,8 +1160,8 @@ msgstr ""
|
|||||||
#: netbox/dcim/forms/bulk_import.py:365 netbox/dcim/forms/bulk_import.py:597
|
#: netbox/dcim/forms/bulk_import.py:365 netbox/dcim/forms/bulk_import.py:597
|
||||||
#: netbox/dcim/forms/bulk_import.py:757 netbox/dcim/forms/bulk_import.py:1250
|
#: netbox/dcim/forms/bulk_import.py:757 netbox/dcim/forms/bulk_import.py:1250
|
||||||
#: netbox/dcim/forms/bulk_import.py:1681 netbox/ipam/forms/bulk_import.py:197
|
#: netbox/dcim/forms/bulk_import.py:1681 netbox/ipam/forms/bulk_import.py:197
|
||||||
#: netbox/ipam/forms/bulk_import.py:265 netbox/ipam/forms/bulk_import.py:301
|
#: netbox/ipam/forms/bulk_import.py:261 netbox/ipam/forms/bulk_import.py:297
|
||||||
#: netbox/ipam/forms/bulk_import.py:512 netbox/ipam/forms/bulk_import.py:525
|
#: netbox/ipam/forms/bulk_import.py:508 netbox/ipam/forms/bulk_import.py:521
|
||||||
#: netbox/virtualization/forms/bulk_import.py:62
|
#: netbox/virtualization/forms/bulk_import.py:62
|
||||||
#: netbox/virtualization/forms/bulk_import.py:93
|
#: netbox/virtualization/forms/bulk_import.py:93
|
||||||
#: netbox/vpn/forms/bulk_import.py:39 netbox/vpn/forms/bulk_import.py:266
|
#: netbox/vpn/forms/bulk_import.py:39 netbox/vpn/forms/bulk_import.py:266
|
||||||
@@ -1178,9 +1178,9 @@ msgstr ""
|
|||||||
#: netbox/dcim/forms/bulk_import.py:1740 netbox/ipam/forms/bulk_import.py:45
|
#: netbox/dcim/forms/bulk_import.py:1740 netbox/ipam/forms/bulk_import.py:45
|
||||||
#: netbox/ipam/forms/bulk_import.py:74 netbox/ipam/forms/bulk_import.py:102
|
#: netbox/ipam/forms/bulk_import.py:74 netbox/ipam/forms/bulk_import.py:102
|
||||||
#: netbox/ipam/forms/bulk_import.py:122 netbox/ipam/forms/bulk_import.py:142
|
#: netbox/ipam/forms/bulk_import.py:122 netbox/ipam/forms/bulk_import.py:142
|
||||||
#: netbox/ipam/forms/bulk_import.py:171 netbox/ipam/forms/bulk_import.py:260
|
#: netbox/ipam/forms/bulk_import.py:171 netbox/ipam/forms/bulk_import.py:256
|
||||||
#: netbox/ipam/forms/bulk_import.py:296 netbox/ipam/forms/bulk_import.py:476
|
#: netbox/ipam/forms/bulk_import.py:292 netbox/ipam/forms/bulk_import.py:472
|
||||||
#: netbox/ipam/forms/bulk_import.py:507
|
#: netbox/ipam/forms/bulk_import.py:503
|
||||||
#: netbox/virtualization/forms/bulk_import.py:76
|
#: netbox/virtualization/forms/bulk_import.py:76
|
||||||
#: netbox/virtualization/forms/bulk_import.py:130
|
#: netbox/virtualization/forms/bulk_import.py:130
|
||||||
#: netbox/vpn/forms/bulk_import.py:63 netbox/wireless/forms/bulk_import.py:61
|
#: netbox/vpn/forms/bulk_import.py:63 netbox/wireless/forms/bulk_import.py:61
|
||||||
@@ -1224,7 +1224,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/forms/model_forms.py:1571 netbox/dcim/forms/model_forms.py:1738
|
#: netbox/dcim/forms/model_forms.py:1571 netbox/dcim/forms/model_forms.py:1738
|
||||||
#: netbox/dcim/forms/model_forms.py:1773 netbox/dcim/forms/model_forms.py:1903
|
#: netbox/dcim/forms/model_forms.py:1773 netbox/dcim/forms/model_forms.py:1903
|
||||||
#: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1169
|
#: netbox/dcim/tables/connections.py:65 netbox/dcim/tables/devices.py:1169
|
||||||
#: netbox/ipam/forms/bulk_import.py:324 netbox/ipam/forms/model_forms.py:291
|
#: netbox/ipam/forms/bulk_import.py:320 netbox/ipam/forms/model_forms.py:291
|
||||||
#: netbox/ipam/forms/model_forms.py:300 netbox/ipam/tables/fhrp.py:64
|
#: netbox/ipam/forms/model_forms.py:300 netbox/ipam/tables/fhrp.py:64
|
||||||
#: netbox/ipam/tables/ip.py:330 netbox/ipam/tables/vlans.py:148
|
#: netbox/ipam/tables/ip.py:330 netbox/ipam/tables/vlans.py:148
|
||||||
#: netbox/templates/circuits/inc/circuit_termination_fields.html:52
|
#: netbox/templates/circuits/inc/circuit_termination_fields.html:52
|
||||||
@@ -1389,7 +1389,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/forms/bulk_import.py:104 netbox/dcim/forms/model_forms.py:126
|
#: netbox/dcim/forms/bulk_import.py:104 netbox/dcim/forms/model_forms.py:126
|
||||||
#: netbox/dcim/tables/sites.py:103 netbox/extras/forms/filtersets.py:582
|
#: netbox/dcim/tables/sites.py:103 netbox/extras/forms/filtersets.py:582
|
||||||
#: netbox/ipam/filtersets.py:982 netbox/ipam/forms/bulk_edit.py:488
|
#: netbox/ipam/filtersets.py:982 netbox/ipam/forms/bulk_edit.py:488
|
||||||
#: netbox/ipam/forms/bulk_import.py:496 netbox/ipam/forms/model_forms.py:571
|
#: netbox/ipam/forms/bulk_import.py:492 netbox/ipam/forms/model_forms.py:571
|
||||||
#: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:94
|
#: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:94
|
||||||
#: netbox/ipam/tables/vlans.py:205
|
#: netbox/ipam/tables/vlans.py:205
|
||||||
#: netbox/templates/circuits/circuitgroupassignment.html:22
|
#: netbox/templates/circuits/circuitgroupassignment.html:22
|
||||||
@@ -1984,7 +1984,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/tables/devices.py:862 netbox/dcim/tables/devices.py:921
|
#: netbox/dcim/tables/devices.py:862 netbox/dcim/tables/devices.py:921
|
||||||
#: netbox/dcim/tables/devices.py:989 netbox/dcim/tables/devices.py:1118
|
#: netbox/dcim/tables/devices.py:989 netbox/dcim/tables/devices.py:1118
|
||||||
#: netbox/dcim/tables/modules.py:87 netbox/extras/forms/filtersets.py:389
|
#: netbox/dcim/tables/modules.py:87 netbox/extras/forms/filtersets.py:389
|
||||||
#: netbox/ipam/forms/bulk_import.py:310 netbox/ipam/forms/filtersets.py:626
|
#: netbox/ipam/forms/bulk_import.py:306 netbox/ipam/forms/filtersets.py:626
|
||||||
#: netbox/ipam/forms/model_forms.py:334 netbox/ipam/tables/vlans.py:159
|
#: netbox/ipam/forms/model_forms.py:334 netbox/ipam/tables/vlans.py:159
|
||||||
#: netbox/templates/circuits/virtualcircuittermination.html:56
|
#: netbox/templates/circuits/virtualcircuittermination.html:56
|
||||||
#: netbox/templates/dcim/consoleport.html:20
|
#: netbox/templates/dcim/consoleport.html:20
|
||||||
@@ -3185,7 +3185,7 @@ msgstr ""
|
|||||||
#: netbox/dcim/tables/devices.py:719 netbox/dcim/tables/devices.py:929
|
#: netbox/dcim/tables/devices.py:719 netbox/dcim/tables/devices.py:929
|
||||||
#: netbox/dcim/tables/devices.py:1016 netbox/dcim/tables/devices.py:1175
|
#: netbox/dcim/tables/devices.py:1016 netbox/dcim/tables/devices.py:1175
|
||||||
#: netbox/dcim/tables/sites.py:28 netbox/dcim/tables/sites.py:62
|
#: netbox/dcim/tables/sites.py:28 netbox/dcim/tables/sites.py:62
|
||||||
#: netbox/dcim/tables/sites.py:147 netbox/ipam/forms/bulk_import.py:582
|
#: netbox/dcim/tables/sites.py:147 netbox/ipam/forms/bulk_import.py:578
|
||||||
#: netbox/ipam/forms/model_forms.py:770 netbox/ipam/tables/fhrp.py:59
|
#: netbox/ipam/forms/model_forms.py:770 netbox/ipam/tables/fhrp.py:59
|
||||||
#: netbox/ipam/tables/ip.py:336 netbox/ipam/tables/services.py:45
|
#: netbox/ipam/tables/ip.py:336 netbox/ipam/tables/services.py:45
|
||||||
#: netbox/templates/dcim/devicerole.html:34
|
#: netbox/templates/dcim/devicerole.html:34
|
||||||
@@ -3963,7 +3963,7 @@ msgid "Is assigned"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/filtersets.py:1826 netbox/dcim/forms/bulk_import.py:1355
|
#: netbox/dcim/filtersets.py:1826 netbox/dcim/forms/bulk_import.py:1355
|
||||||
#: netbox/ipam/forms/bulk_import.py:338
|
#: netbox/ipam/forms/bulk_import.py:334
|
||||||
msgid "Is primary"
|
msgid "Is primary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -3991,7 +3991,7 @@ msgstr ""
|
|||||||
#: netbox/ipam/filtersets.py:579 netbox/ipam/filtersets.py:590
|
#: netbox/ipam/filtersets.py:579 netbox/ipam/filtersets.py:590
|
||||||
#: netbox/ipam/forms/bulk_edit.py:226 netbox/ipam/forms/bulk_edit.py:282
|
#: netbox/ipam/forms/bulk_edit.py:226 netbox/ipam/forms/bulk_edit.py:282
|
||||||
#: netbox/ipam/forms/bulk_edit.py:329 netbox/ipam/forms/bulk_import.py:160
|
#: netbox/ipam/forms/bulk_edit.py:329 netbox/ipam/forms/bulk_import.py:160
|
||||||
#: netbox/ipam/forms/bulk_import.py:249 netbox/ipam/forms/bulk_import.py:285
|
#: netbox/ipam/forms/bulk_import.py:245 netbox/ipam/forms/bulk_import.py:281
|
||||||
#: netbox/ipam/forms/filtersets.py:69 netbox/ipam/forms/filtersets.py:180
|
#: netbox/ipam/forms/filtersets.py:69 netbox/ipam/forms/filtersets.py:180
|
||||||
#: netbox/ipam/forms/filtersets.py:332 netbox/ipam/forms/model_forms.py:66
|
#: netbox/ipam/forms/filtersets.py:332 netbox/ipam/forms/model_forms.py:66
|
||||||
#: netbox/ipam/forms/model_forms.py:209 netbox/ipam/forms/model_forms.py:257
|
#: netbox/ipam/forms/model_forms.py:209 netbox/ipam/forms/model_forms.py:257
|
||||||
@@ -4856,7 +4856,7 @@ msgid "available options"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/bulk_import.py:138 netbox/dcim/forms/bulk_import.py:633
|
#: netbox/dcim/forms/bulk_import.py:138 netbox/dcim/forms/bulk_import.py:633
|
||||||
#: netbox/dcim/forms/bulk_import.py:1650 netbox/ipam/forms/bulk_import.py:493
|
#: netbox/dcim/forms/bulk_import.py:1650 netbox/ipam/forms/bulk_import.py:489
|
||||||
#: netbox/virtualization/forms/bulk_import.py:69
|
#: netbox/virtualization/forms/bulk_import.py:69
|
||||||
#: netbox/virtualization/forms/bulk_import.py:100
|
#: netbox/virtualization/forms/bulk_import.py:100
|
||||||
msgid "Assigned site"
|
msgid "Assigned site"
|
||||||
@@ -5168,7 +5168,7 @@ msgid "Assigned Q-in-Q Service VLAN ID (filtered by VLAN group)"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/bulk_import.py:1028 netbox/ipam/forms/bulk_import.py:164
|
#: netbox/dcim/forms/bulk_import.py:1028 netbox/ipam/forms/bulk_import.py:164
|
||||||
#: netbox/ipam/forms/bulk_import.py:253 netbox/ipam/forms/bulk_import.py:289
|
#: netbox/ipam/forms/bulk_import.py:249 netbox/ipam/forms/bulk_import.py:285
|
||||||
#: netbox/ipam/forms/filtersets.py:210 netbox/ipam/forms/filtersets.py:293
|
#: netbox/ipam/forms/filtersets.py:210 netbox/ipam/forms/filtersets.py:293
|
||||||
#: netbox/ipam/forms/filtersets.py:360
|
#: netbox/ipam/forms/filtersets.py:360
|
||||||
#: netbox/virtualization/forms/bulk_import.py:220
|
#: netbox/virtualization/forms/bulk_import.py:220
|
||||||
@@ -5247,11 +5247,11 @@ msgstr ""
|
|||||||
msgid "Component type must be specified when component name is specified"
|
msgid "Component type must be specified when component name is specified"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/bulk_import.py:1338 netbox/ipam/forms/bulk_import.py:314
|
#: netbox/dcim/forms/bulk_import.py:1338 netbox/ipam/forms/bulk_import.py:310
|
||||||
msgid "Parent device of assigned interface (if any)"
|
msgid "Parent device of assigned interface (if any)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/bulk_import.py:1341 netbox/ipam/forms/bulk_import.py:317
|
#: netbox/dcim/forms/bulk_import.py:1341 netbox/ipam/forms/bulk_import.py:313
|
||||||
#: netbox/virtualization/filtersets.py:259
|
#: netbox/virtualization/filtersets.py:259
|
||||||
#: netbox/virtualization/filtersets.py:310
|
#: netbox/virtualization/filtersets.py:310
|
||||||
#: netbox/virtualization/forms/bulk_edit.py:182
|
#: netbox/virtualization/forms/bulk_edit.py:182
|
||||||
@@ -5265,12 +5265,12 @@ msgstr ""
|
|||||||
msgid "Virtual machine"
|
msgid "Virtual machine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/bulk_import.py:1345 netbox/ipam/forms/bulk_import.py:321
|
#: netbox/dcim/forms/bulk_import.py:1345 netbox/ipam/forms/bulk_import.py:317
|
||||||
msgid "Parent VM of assigned interface (if any)"
|
msgid "Parent VM of assigned interface (if any)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/bulk_import.py:1352 netbox/ipam/filtersets.py:1035
|
#: netbox/dcim/forms/bulk_import.py:1352 netbox/ipam/filtersets.py:1035
|
||||||
#: netbox/ipam/forms/bulk_import.py:328
|
#: netbox/ipam/forms/bulk_import.py:324
|
||||||
msgid "Assigned interface"
|
msgid "Assigned interface"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -5654,7 +5654,7 @@ msgstr ""
|
|||||||
msgid "Please select a {scope_type}."
|
msgid "Please select a {scope_type}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/forms/mixins.py:117 netbox/ipam/forms/bulk_import.py:466
|
#: netbox/dcim/forms/mixins.py:117 netbox/ipam/forms/bulk_import.py:462
|
||||||
msgid "Scope type (app & model)"
|
msgid "Scope type (app & model)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -6404,7 +6404,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: netbox/dcim/models/device_components.py:605
|
#: netbox/dcim/models/device_components.py:605
|
||||||
#: netbox/dcim/tables/devices.py:631 netbox/ipam/forms/bulk_edit.py:521
|
#: netbox/dcim/tables/devices.py:631 netbox/ipam/forms/bulk_edit.py:521
|
||||||
#: netbox/ipam/forms/bulk_import.py:528 netbox/ipam/forms/filtersets.py:587
|
#: netbox/ipam/forms/bulk_import.py:524 netbox/ipam/forms/filtersets.py:587
|
||||||
#: netbox/ipam/forms/model_forms.py:694 netbox/ipam/tables/vlans.py:109
|
#: netbox/ipam/forms/model_forms.py:694 netbox/ipam/tables/vlans.py:109
|
||||||
#: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77
|
#: netbox/templates/dcim/interface.html:86 netbox/templates/ipam/vlan.html:77
|
||||||
#: netbox/templates/virtualization/vminterface.html:60
|
#: netbox/templates/virtualization/vminterface.html:60
|
||||||
@@ -7324,8 +7324,8 @@ msgid "Locally-assigned identifier"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/models/racks.py:305 netbox/ipam/forms/bulk_import.py:204
|
#: netbox/dcim/models/racks.py:305 netbox/ipam/forms/bulk_import.py:204
|
||||||
#: netbox/ipam/forms/bulk_import.py:272 netbox/ipam/forms/bulk_import.py:307
|
#: netbox/ipam/forms/bulk_import.py:268 netbox/ipam/forms/bulk_import.py:303
|
||||||
#: netbox/ipam/forms/bulk_import.py:519
|
#: netbox/ipam/forms/bulk_import.py:515
|
||||||
#: netbox/virtualization/forms/bulk_import.py:123
|
#: netbox/virtualization/forms/bulk_import.py:123
|
||||||
msgid "Functional role"
|
msgid "Functional role"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -7576,7 +7576,7 @@ msgid "U Height"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/tables/devices.py:210 netbox/dcim/tables/devices.py:1128
|
#: netbox/dcim/tables/devices.py:210 netbox/dcim/tables/devices.py:1128
|
||||||
#: netbox/ipam/forms/bulk_import.py:601 netbox/ipam/forms/model_forms.py:317
|
#: netbox/ipam/forms/bulk_import.py:597 netbox/ipam/forms/model_forms.py:317
|
||||||
#: netbox/ipam/forms/model_forms.py:330 netbox/ipam/tables/ip.py:314
|
#: netbox/ipam/forms/model_forms.py:330 netbox/ipam/tables/ip.py:314
|
||||||
#: netbox/ipam/tables/ip.py:381 netbox/ipam/tables/ip.py:391
|
#: netbox/ipam/tables/ip.py:381 netbox/ipam/tables/ip.py:391
|
||||||
#: netbox/ipam/tables/ip.py:414 netbox/templates/ipam/ipaddress.html:11
|
#: netbox/ipam/tables/ip.py:414 netbox/templates/ipam/ipaddress.html:11
|
||||||
@@ -10419,8 +10419,8 @@ msgid "DNS name"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_edit.py:376 netbox/ipam/forms/bulk_edit.py:573
|
#: netbox/ipam/forms/bulk_edit.py:376 netbox/ipam/forms/bulk_edit.py:573
|
||||||
#: netbox/ipam/forms/bulk_import.py:447 netbox/ipam/forms/bulk_import.py:565
|
#: netbox/ipam/forms/bulk_import.py:443 netbox/ipam/forms/bulk_import.py:561
|
||||||
#: netbox/ipam/forms/bulk_import.py:593 netbox/ipam/forms/filtersets.py:414
|
#: netbox/ipam/forms/bulk_import.py:589 netbox/ipam/forms/filtersets.py:414
|
||||||
#: netbox/ipam/forms/filtersets.py:604 netbox/templates/ipam/fhrpgroup.html:22
|
#: netbox/ipam/forms/filtersets.py:604 netbox/templates/ipam/fhrpgroup.html:22
|
||||||
#: netbox/templates/ipam/inc/panels/fhrp_groups.html:24
|
#: netbox/templates/ipam/inc/panels/fhrp_groups.html:24
|
||||||
#: netbox/templates/ipam/service.html:34
|
#: netbox/templates/ipam/service.html:34
|
||||||
@@ -10464,7 +10464,7 @@ msgstr ""
|
|||||||
msgid "VLAN ID ranges"
|
msgid "VLAN ID ranges"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_edit.py:516 netbox/ipam/forms/bulk_import.py:522
|
#: netbox/ipam/forms/bulk_edit.py:516 netbox/ipam/forms/bulk_import.py:518
|
||||||
#: netbox/ipam/forms/filtersets.py:579 netbox/ipam/models/vlans.py:250
|
#: netbox/ipam/forms/filtersets.py:579 netbox/ipam/models/vlans.py:250
|
||||||
#: netbox/ipam/tables/vlans.py:106
|
#: netbox/ipam/tables/vlans.py:106
|
||||||
msgid "Q-in-Q role"
|
msgid "Q-in-Q role"
|
||||||
@@ -10478,7 +10478,7 @@ msgstr ""
|
|||||||
msgid "Site & Group"
|
msgid "Site & Group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_edit.py:557 netbox/ipam/forms/bulk_import.py:552
|
#: netbox/ipam/forms/bulk_edit.py:557 netbox/ipam/forms/bulk_import.py:548
|
||||||
#: netbox/ipam/forms/model_forms.py:726 netbox/ipam/tables/vlans.py:259
|
#: netbox/ipam/forms/model_forms.py:726 netbox/ipam/tables/vlans.py:259
|
||||||
#: netbox/templates/ipam/vlantranslationrule.html:14
|
#: netbox/templates/ipam/vlantranslationrule.html:14
|
||||||
#: netbox/vpn/forms/model_forms.py:322 netbox/vpn/forms/model_forms.py:359
|
#: netbox/vpn/forms/model_forms.py:322 netbox/vpn/forms/model_forms.py:359
|
||||||
@@ -10523,86 +10523,86 @@ msgstr ""
|
|||||||
msgid "Scope ID"
|
msgid "Scope ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:331 netbox/ipam/forms/filtersets.py:636
|
#: netbox/ipam/forms/bulk_import.py:327 netbox/ipam/forms/filtersets.py:636
|
||||||
#: netbox/ipam/forms/model_forms.py:306 netbox/ipam/forms/model_forms.py:336
|
#: netbox/ipam/forms/model_forms.py:306 netbox/ipam/forms/model_forms.py:336
|
||||||
#: netbox/ipam/forms/model_forms.py:517 netbox/templates/ipam/fhrpgroup.html:19
|
#: netbox/ipam/forms/model_forms.py:517 netbox/templates/ipam/fhrpgroup.html:19
|
||||||
msgid "FHRP Group"
|
msgid "FHRP Group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:335
|
#: netbox/ipam/forms/bulk_import.py:331
|
||||||
msgid "Assigned FHRP Group name"
|
msgid "Assigned FHRP Group name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:339
|
#: netbox/ipam/forms/bulk_import.py:335
|
||||||
msgid "Make this the primary IP for the assigned device"
|
msgid "Make this the primary IP for the assigned device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:343
|
#: netbox/ipam/forms/bulk_import.py:339
|
||||||
msgid "Is out-of-band"
|
msgid "Is out-of-band"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:344
|
#: netbox/ipam/forms/bulk_import.py:340
|
||||||
msgid "Designate this as the out-of-band IP address for the assigned device"
|
msgid "Designate this as the out-of-band IP address for the assigned device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:398
|
#: netbox/ipam/forms/bulk_import.py:394
|
||||||
msgid "No device or virtual machine specified; cannot set as primary IP"
|
msgid "No device or virtual machine specified; cannot set as primary IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:402
|
#: netbox/ipam/forms/bulk_import.py:398
|
||||||
msgid "No device specified; cannot set as out-of-band IP"
|
msgid "No device specified; cannot set as out-of-band IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:406
|
#: netbox/ipam/forms/bulk_import.py:402
|
||||||
msgid "Cannot set out-of-band IP for virtual machines"
|
msgid "Cannot set out-of-band IP for virtual machines"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:410
|
#: netbox/ipam/forms/bulk_import.py:406
|
||||||
msgid "No interface specified; cannot set as primary IP"
|
msgid "No interface specified; cannot set as primary IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:414
|
#: netbox/ipam/forms/bulk_import.py:410
|
||||||
msgid "No interface specified; cannot set as out-of-band IP"
|
msgid "No interface specified; cannot set as out-of-band IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:451
|
#: netbox/ipam/forms/bulk_import.py:447
|
||||||
msgid "Auth type"
|
msgid "Auth type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:500
|
#: netbox/ipam/forms/bulk_import.py:496
|
||||||
msgid "Assigned VLAN group"
|
msgid "Assigned VLAN group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:532
|
#: netbox/ipam/forms/bulk_import.py:528
|
||||||
msgid "Service VLAN (for Q-in-Q/802.1ad customer VLANs)"
|
msgid "Service VLAN (for Q-in-Q/802.1ad customer VLANs)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:555 netbox/ipam/models/vlans.py:369
|
#: netbox/ipam/forms/bulk_import.py:551 netbox/ipam/models/vlans.py:369
|
||||||
msgid "VLAN translation policy"
|
msgid "VLAN translation policy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:567 netbox/ipam/forms/bulk_import.py:595
|
#: netbox/ipam/forms/bulk_import.py:563 netbox/ipam/forms/bulk_import.py:591
|
||||||
msgid "IP protocol"
|
msgid "IP protocol"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:579
|
#: netbox/ipam/forms/bulk_import.py:575
|
||||||
msgid "Parent type (app & model)"
|
msgid "Parent type (app & model)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:586
|
#: netbox/ipam/forms/bulk_import.py:582
|
||||||
msgid "Parent object name"
|
msgid "Parent object name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:590
|
#: netbox/ipam/forms/bulk_import.py:586
|
||||||
msgid "Parent object ID"
|
msgid "Parent object ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:642
|
#: netbox/ipam/forms/bulk_import.py:638
|
||||||
msgid ""
|
msgid ""
|
||||||
"One of parent or parent_object_id must be included with parent_object_type"
|
"One of parent or parent_object_id must be included with parent_object_type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/forms/bulk_import.py:655
|
#: netbox/ipam/forms/bulk_import.py:651
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{ip} is not assigned to this parent."
|
msgid "{ip} is not assigned to this parent."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Reference in New Issue
Block a user