Merge branch 'main' into 18433-fix-macaddress-primary-for-interface

This commit is contained in:
Daniel Sheppard 2025-01-27 12:11:37 -06:00 committed by GitHub
commit ee5d7cfe31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 175 additions and 121 deletions

View File

@ -1,5 +1,6 @@
--- ---
name: ✨ Feature Request name: ✨ Feature Request
type: Feature
description: Propose a new NetBox feature or enhancement description: Propose a new NetBox feature or enhancement
labels: ["type: feature", "status: needs triage"] labels: ["type: feature", "status: needs triage"]
body: body:

View File

@ -1,5 +1,6 @@
--- ---
name: 🐛 Bug Report name: 🐛 Bug Report
type: Bug
description: Report a reproducible bug in the current release of NetBox description: Report a reproducible bug in the current release of NetBox
labels: ["type: bug", "status: needs triage"] labels: ["type: bug", "status: needs triage"]
body: body:

View File

@ -1,5 +1,6 @@
--- ---
name: 📖 Documentation Change name: 📖 Documentation Change
type: Documentation
description: Suggest an addition or modification to the NetBox documentation description: Suggest an addition or modification to the NetBox documentation
labels: ["type: documentation", "status: needs triage"] labels: ["type: documentation", "status: needs triage"]
body: body:

View File

@ -1,5 +1,6 @@
--- ---
name: 🌍 Translation name: 🌍 Translation
type: Translation
description: Request support for a new language in the user interface description: Request support for a new language in the user interface
labels: ["type: translation"] labels: ["type: translation"]
body: body:

View File

@ -1,5 +1,6 @@
--- ---
name: 🏡 Housekeeping name: 🏡 Housekeeping
type: Housekeeping
description: A change pertaining to the codebase itself (developers only) description: A change pertaining to the codebase itself (developers only)
labels: ["type: housekeeping"] labels: ["type: housekeeping"]
body: body:

View File

@ -1,5 +1,6 @@
--- ---
name: 🗑️ Deprecation name: 🗑️ Deprecation
type: Deprecation
description: The removal of an existing feature or resource description: The removal of an existing feature or resource
labels: ["type: deprecation"] labels: ["type: deprecation"]
body: body:

View File

@ -3,11 +3,15 @@ name: CI
on: on:
push: push:
paths-ignore: paths-ignore:
- '.github/ISSUE_TEMPLATE/**'
- '.github/PULL_REQUEST_TEMPLATE.md'
- 'contrib/**' - 'contrib/**'
- 'docs/**' - 'docs/**'
- 'netbox/translations/**' - 'netbox/translations/**'
pull_request: pull_request:
paths-ignore: paths-ignore:
- '.github/ISSUE_TEMPLATE/**'
- '.github/PULL_REQUEST_TEMPLATE.md'
- 'contrib/**' - 'contrib/**'
- 'docs/**' - 'docs/**'
- 'netbox/translations/**' - 'netbox/translations/**'

View File

@ -46,7 +46,7 @@ Regions will always be listed alphabetically by name within each parent, and the
Like regions, site groups can be arranged in a recursive hierarchy for grouping sites. However, whereas regions are intended for geographic organization, site groups may be used for functional grouping. For example, you might classify sites as corporate, branch, or customer sites in addition to where they are physically located. Like regions, site groups can be arranged in a recursive hierarchy for grouping sites. However, whereas regions are intended for geographic organization, site groups may be used for functional grouping. For example, you might classify sites as corporate, branch, or customer sites in addition to where they are physically located.
The use of both regions and site groups affords to independent but complementary dimensions across which sites can be organized. The use of both regions and site groups affords two independent but complementary dimensions across which sites can be organized.
## Sites ## Sites

View File

@ -62,6 +62,9 @@ GRANT CREATE ON SCHEMA public TO netbox;
!!! danger "Use a strong password" !!! danger "Use a strong password"
**Do not use the password from the example.** Choose a strong, random password to ensure secure database authentication for your NetBox installation. **Do not use the password from the example.** Choose a strong, random password to ensure secure database authentication for your NetBox installation.
!!! danger "Use UTF8 encoding"
Make sure that your database uses `UTF8` encoding (the default for new installations). Especially do not use `SQL_ASCII` encoding, as it can lead to unpredictable and unrecoverable errors. Enter `\l` to check your encoding.
Once complete, enter `\q` to exit the PostgreSQL shell. Once complete, enter `\q` to exit the PostgreSQL shell.
## Verify Service Status ## Verify Service Status

View File

@ -1,4 +1,3 @@
# Generated by Django 5.0.9 on 2024-10-21 17:34
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -16,7 +15,7 @@ def populate_denormalized_fields(apps, schema_editor):
termination._site_id = termination.site_id termination._site_id = termination.site_id
# Note: Location cannot be set prior to migration # Note: Location cannot be set prior to migration
CircuitTermination.objects.bulk_update(terminations, ['_region', '_site_group', '_site']) CircuitTermination.objects.bulk_update(terminations, ['_region', '_site_group', '_site'], batch_size=100)
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -53,10 +53,10 @@ class ScopedFilterSet(BaseFilterSet):
label=_('Site (slug)'), label=_('Site (slug)'),
) )
location_id = TreeNodeMultipleChoiceFilter( location_id = TreeNodeMultipleChoiceFilter(
queryset=Location.objects.all(), queryset=Location.objects.all(),
field_name='_location', field_name='_location',
lookup_expr='in', lookup_expr='in',
label=_('Location (ID)'), label=_('Location (ID)'),
) )
location = TreeNodeMultipleChoiceFilter( location = TreeNodeMultipleChoiceFilter(
queryset=Location.objects.all(), queryset=Location.objects.all(),

View File

@ -1652,8 +1652,8 @@ class MACAddressFilterSet(NetBoxModelFilterSet):
if not value.strip(): if not value.strip():
return queryset return queryset
qs_filter = ( qs_filter = (
Q(mac_address__icontains=value) | Q(mac_address__icontains=value) |
Q(description__icontains=value) Q(description__icontains=value)
) )
return queryset.filter(qs_filter) return queryset.filter(qs_filter)

View File

@ -1810,6 +1810,11 @@ class MACAddressForm(NetBoxModelForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if instance and instance.assigned_object and instance.assigned_object.primary_mac_address:
if instance.assigned_object.primary_mac_address.pk == instance.pk:
self.fields['interface'].disabled = True
self.fields['vminterface'].disabled = True
def clean(self): def clean(self):
super().clean() super().clean()

View File

@ -15,6 +15,7 @@ from django.urls import reverse
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 core.models import ObjectType
from dcim.choices import * from dcim.choices import *
from dcim.constants import * from dcim.constants import *
from dcim.fields import MACAddressField from dcim.fields import MACAddressField
@ -1523,9 +1524,33 @@ class MACAddress(PrimaryModel):
def __str__(self): def __str__(self):
return str(self.mac_address) return str(self.mac_address)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Denote the original assigned object (if any) for validation in clean()
self._original_assigned_object_id = self.__dict__.get('assigned_object_id')
self._original_assigned_object_type_id = self.__dict__.get('assigned_object_type_id')
@cached_property @cached_property
def is_primary(self): def is_primary(self):
if self.assigned_object and hasattr(self.assigned_object, 'primary_mac_address'): if self.assigned_object and hasattr(self.assigned_object, 'primary_mac_address'):
if self.assigned_object.primary_mac_address and self.assigned_object.primary_mac_address.pk == self.pk: if self.assigned_object.primary_mac_address and self.assigned_object.primary_mac_address.pk == self.pk:
return True return True
return False return False
def clean(self, *args, **kwargs):
super().clean()
if self._original_assigned_object_id and self._original_assigned_object_type_id:
assigned_object = self.assigned_object
ct = ObjectType.objects.get_for_id(self._original_assigned_object_type_id)
original_assigned_object = ct.get_object_for_this_type(pk=self._original_assigned_object_id)
if original_assigned_object.primary_mac_address:
if not assigned_object:
raise ValidationError(
_("Cannot unassign MAC Address while it is designated as the primary MAC for an object")
)
elif original_assigned_object != assigned_object:
raise ValidationError(
_("Cannot reassign MAC Address while it is designated as the primary MAC for an object")
)

View File

@ -705,7 +705,7 @@ class DeviceInterfaceTable(InterfaceTable):
model = models.Interface model = models.Interface
fields = ( fields = (
'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag', 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag',
'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'mgmt_only', 'mtu', 'mode', 'primary_mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link',
'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses',
'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'actions', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'actions',

View File

@ -660,8 +660,8 @@ class CustomFieldAPITest(APITestCase):
CustomField( CustomField(
type=CustomFieldTypeChoices.TYPE_BOOLEAN, type=CustomFieldTypeChoices.TYPE_BOOLEAN,
name='boolean_field', name='boolean_field',
default=False) default=False
, ),
CustomField( CustomField(
type=CustomFieldTypeChoices.TYPE_DATE, type=CustomFieldTypeChoices.TYPE_DATE,
name='date_field', name='date_field',

View File

@ -15,7 +15,7 @@ def populate_denormalized_fields(apps, schema_editor):
prefix._site_id = prefix.site_id prefix._site_id = prefix.site_id
# Note: Location cannot be set prior to migration # Note: Location cannot be set prior to migration
Prefix.objects.bulk_update(prefixes, ['_region', '_site_group', '_site']) Prefix.objects.bulk_update(prefixes, ['_region', '_site_group', '_site'], batch_size=100)
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -361,7 +361,7 @@ class VLANTranslationRule(NetBoxModel):
) )
local_vid = models.PositiveSmallIntegerField( local_vid = models.PositiveSmallIntegerField(
verbose_name=_('Local VLAN ID'), verbose_name=_('Local VLAN ID'),
validators=( validators=(
MinValueValidator(VLAN_VID_MIN), MinValueValidator(VLAN_VID_MIN),
MaxValueValidator(VLAN_VID_MAX) MaxValueValidator(VLAN_VID_MAX)
), ),
@ -369,7 +369,7 @@ class VLANTranslationRule(NetBoxModel):
) )
remote_vid = models.PositiveSmallIntegerField( remote_vid = models.PositiveSmallIntegerField(
verbose_name=_('Remote VLAN ID'), verbose_name=_('Remote VLAN ID'),
validators=( validators=(
MinValueValidator(VLAN_VID_MIN), MinValueValidator(VLAN_VID_MIN),
MaxValueValidator(VLAN_VID_MAX) MaxValueValidator(VLAN_VID_MAX)
), ),

View File

@ -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-01-18 05:01+0000\n" "POT-Creation-Date: 2025-01-24 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"
@ -1402,7 +1402,7 @@ msgstr ""
#: netbox/dcim/models/device_components.py:1024 #: netbox/dcim/models/device_components.py:1024
#: netbox/dcim/models/device_components.py:1095 #: netbox/dcim/models/device_components.py:1095
#: netbox/dcim/models/device_components.py:1241 #: netbox/dcim/models/device_components.py:1241
#: netbox/dcim/models/devices.py:477 netbox/dcim/models/racks.py:221 #: netbox/dcim/models/devices.py:478 netbox/dcim/models/racks.py:221
#: netbox/extras/models/tags.py:28 #: netbox/extras/models/tags.py:28
msgid "color" msgid "color"
msgstr "" msgstr ""
@ -1429,8 +1429,8 @@ msgstr ""
#: netbox/circuits/models/virtual_circuits.py:59 netbox/core/models/data.py:52 #: netbox/circuits/models/virtual_circuits.py:59 netbox/core/models/data.py:52
#: netbox/core/models/jobs.py:85 netbox/dcim/models/cables.py:49 #: netbox/core/models/jobs.py:85 netbox/dcim/models/cables.py:49
#: netbox/dcim/models/device_components.py:1281 #: netbox/dcim/models/device_components.py:1281
#: netbox/dcim/models/devices.py:644 netbox/dcim/models/devices.py:1176 #: netbox/dcim/models/devices.py:645 netbox/dcim/models/devices.py:1177
#: netbox/dcim/models/devices.py:1403 netbox/dcim/models/power.py:94 #: netbox/dcim/models/devices.py:1404 netbox/dcim/models/power.py:94
#: netbox/dcim/models/racks.py:288 netbox/dcim/models/sites.py:154 #: netbox/dcim/models/racks.py:288 netbox/dcim/models/sites.py:154
#: netbox/dcim/models/sites.py:270 netbox/ipam/models/ip.py:237 #: netbox/dcim/models/sites.py:270 netbox/ipam/models/ip.py:237
#: netbox/ipam/models/ip.py:508 netbox/ipam/models/ip.py:729 #: netbox/ipam/models/ip.py:508 netbox/ipam/models/ip.py:729
@ -1560,8 +1560,8 @@ msgstr ""
#: netbox/circuits/models/providers.py:98 netbox/core/models/data.py:39 #: netbox/circuits/models/providers.py:98 netbox/core/models/data.py:39
#: netbox/core/models/jobs.py:46 #: netbox/core/models/jobs.py:46
#: netbox/dcim/models/device_component_templates.py:43 #: netbox/dcim/models/device_component_templates.py:43
#: netbox/dcim/models/device_components.py:52 netbox/dcim/models/devices.py:588 #: netbox/dcim/models/device_components.py:52 netbox/dcim/models/devices.py:589
#: netbox/dcim/models/devices.py:1335 netbox/dcim/models/devices.py:1398 #: netbox/dcim/models/devices.py:1336 netbox/dcim/models/devices.py:1399
#: netbox/dcim/models/power.py:38 netbox/dcim/models/power.py:89 #: netbox/dcim/models/power.py:38 netbox/dcim/models/power.py:89
#: netbox/dcim/models/racks.py:257 netbox/dcim/models/sites.py:142 #: netbox/dcim/models/racks.py:257 netbox/dcim/models/sites.py:142
#: netbox/extras/models/configs.py:36 netbox/extras/models/configs.py:215 #: netbox/extras/models/configs.py:36 netbox/extras/models/configs.py:215
@ -1593,7 +1593,7 @@ msgstr ""
msgid "Full name of the provider" msgid "Full name of the provider"
msgstr "" msgstr ""
#: netbox/circuits/models/providers.py:28 netbox/dcim/models/devices.py:87 #: netbox/circuits/models/providers.py:28 netbox/dcim/models/devices.py:88
#: netbox/dcim/models/racks.py:137 netbox/dcim/models/sites.py:149 #: netbox/dcim/models/racks.py:137 netbox/dcim/models/sites.py:149
#: netbox/extras/models/models.py:506 netbox/ipam/models/asns.py:23 #: netbox/extras/models/models.py:506 netbox/ipam/models/asns.py:23
#: netbox/ipam/models/vlans.py:42 netbox/netbox/models/__init__.py:145 #: netbox/ipam/models/vlans.py:42 netbox/netbox/models/__init__.py:145
@ -3532,7 +3532,7 @@ msgstr ""
#: netbox/dcim/filtersets.py:1104 netbox/dcim/forms/filtersets.py:819 #: netbox/dcim/filtersets.py:1104 netbox/dcim/forms/filtersets.py:819
#: netbox/dcim/forms/filtersets.py:1390 netbox/dcim/forms/filtersets.py:1586 #: netbox/dcim/forms/filtersets.py:1390 netbox/dcim/forms/filtersets.py:1586
#: netbox/dcim/forms/filtersets.py:1591 netbox/dcim/forms/model_forms.py:1762 #: netbox/dcim/forms/filtersets.py:1591 netbox/dcim/forms/model_forms.py:1762
#: netbox/dcim/models/devices.py:1499 netbox/dcim/models/devices.py:1520 #: netbox/dcim/models/devices.py:1500 netbox/dcim/models/devices.py:1521
#: netbox/virtualization/filtersets.py:196 #: netbox/virtualization/filtersets.py:196
#: netbox/virtualization/filtersets.py:268 #: netbox/virtualization/filtersets.py:268
#: netbox/virtualization/forms/filtersets.py:177 #: netbox/virtualization/forms/filtersets.py:177
@ -4125,7 +4125,7 @@ msgstr ""
msgid "Chassis" msgid "Chassis"
msgstr "" msgstr ""
#: netbox/dcim/forms/bulk_edit.py:619 netbox/dcim/models/devices.py:482 #: netbox/dcim/forms/bulk_edit.py:619 netbox/dcim/models/devices.py:483
#: netbox/dcim/tables/devices.py:78 #: netbox/dcim/tables/devices.py:78
msgid "VM role" msgid "VM role"
msgstr "" msgstr ""
@ -5234,7 +5234,7 @@ msgstr ""
msgid "Device Role" msgid "Device Role"
msgstr "" msgstr ""
#: netbox/dcim/forms/model_forms.py:500 netbox/dcim/models/devices.py:634 #: netbox/dcim/forms/model_forms.py:500 netbox/dcim/models/devices.py:635
msgid "The lowest-numbered unit occupied by the device" msgid "The lowest-numbered unit occupied by the device"
msgstr "" msgstr ""
@ -5417,7 +5417,7 @@ msgstr ""
msgid "Virtual Machine" msgid "Virtual Machine"
msgstr "" msgstr ""
#: netbox/dcim/forms/model_forms.py:1822 #: netbox/dcim/forms/model_forms.py:1827
msgid "A MAC address can only be assigned to a single object." msgid "A MAC address can only be assigned to a single object."
msgstr "" msgstr ""
@ -6139,7 +6139,7 @@ msgid "module bays"
msgstr "" msgstr ""
#: netbox/dcim/models/device_components.py:1178 #: netbox/dcim/models/device_components.py:1178
#: netbox/dcim/models/devices.py:1224 #: netbox/dcim/models/devices.py:1225
msgid "A module bay cannot belong to a module installed within it." msgid "A module bay cannot belong to a module installed within it."
msgstr "" msgstr ""
@ -6175,14 +6175,14 @@ msgid "inventory item roles"
msgstr "" msgstr ""
#: netbox/dcim/models/device_components.py:1308 #: netbox/dcim/models/device_components.py:1308
#: netbox/dcim/models/devices.py:597 netbox/dcim/models/devices.py:1184 #: netbox/dcim/models/devices.py:598 netbox/dcim/models/devices.py:1185
#: netbox/dcim/models/racks.py:304 #: netbox/dcim/models/racks.py:304
#: netbox/virtualization/models/virtualmachines.py:126 #: netbox/virtualization/models/virtualmachines.py:126
msgid "serial number" msgid "serial number"
msgstr "" msgstr ""
#: netbox/dcim/models/device_components.py:1316 #: netbox/dcim/models/device_components.py:1316
#: netbox/dcim/models/devices.py:605 netbox/dcim/models/devices.py:1191 #: netbox/dcim/models/devices.py:606 netbox/dcim/models/devices.py:1192
#: netbox/dcim/models/racks.py:311 #: netbox/dcim/models/racks.py:311
msgid "asset tag" msgid "asset tag"
msgstr "" msgstr ""
@ -6223,377 +6223,389 @@ msgstr ""
msgid "Cannot assign inventory item to component on another device" msgid "Cannot assign inventory item to component on another device"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:58 #: netbox/dcim/models/devices.py:59
msgid "manufacturer" msgid "manufacturer"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:59 #: netbox/dcim/models/devices.py:60
msgid "manufacturers" msgid "manufacturers"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:83 netbox/dcim/models/devices.py:382 #: netbox/dcim/models/devices.py:84 netbox/dcim/models/devices.py:383
#: netbox/dcim/models/racks.py:133 #: netbox/dcim/models/racks.py:133
msgid "model" msgid "model"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:96 #: netbox/dcim/models/devices.py:97
msgid "default platform" msgid "default platform"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:99 netbox/dcim/models/devices.py:386 #: netbox/dcim/models/devices.py:100 netbox/dcim/models/devices.py:387
msgid "part number" msgid "part number"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:102 netbox/dcim/models/devices.py:389 #: netbox/dcim/models/devices.py:103 netbox/dcim/models/devices.py:390
msgid "Discrete part number (optional)" msgid "Discrete part number (optional)"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:108 netbox/dcim/models/racks.py:53 #: netbox/dcim/models/devices.py:109 netbox/dcim/models/racks.py:53
msgid "height (U)" msgid "height (U)"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:112 #: netbox/dcim/models/devices.py:113
msgid "exclude from utilization" msgid "exclude from utilization"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:113 #: netbox/dcim/models/devices.py:114
msgid "Devices of this type are excluded when calculating rack utilization." msgid "Devices of this type are excluded when calculating rack utilization."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:117 #: netbox/dcim/models/devices.py:118
msgid "is full depth" msgid "is full depth"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:118 #: netbox/dcim/models/devices.py:119
msgid "Device consumes both front and rear rack faces." msgid "Device consumes both front and rear rack faces."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:125 #: netbox/dcim/models/devices.py:126
msgid "parent/child status" msgid "parent/child status"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:126 #: netbox/dcim/models/devices.py:127
msgid "" msgid ""
"Parent devices house child devices in device bays. Leave blank if this " "Parent devices house child devices in device bays. Leave blank if this "
"device type is neither a parent nor a child." "device type is neither a parent nor a child."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:130 netbox/dcim/models/devices.py:392 #: netbox/dcim/models/devices.py:131 netbox/dcim/models/devices.py:393
#: netbox/dcim/models/devices.py:650 netbox/dcim/models/racks.py:315 #: netbox/dcim/models/devices.py:651 netbox/dcim/models/racks.py:315
msgid "airflow" msgid "airflow"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:207 #: netbox/dcim/models/devices.py:208
msgid "device type" msgid "device type"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:208 #: netbox/dcim/models/devices.py:209
msgid "device types" msgid "device types"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:290 #: netbox/dcim/models/devices.py:291
msgid "U height must be in increments of 0.5 rack units." msgid "U height must be in increments of 0.5 rack units."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:307 #: netbox/dcim/models/devices.py:308
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Device {device} in rack {rack} does not have sufficient space to accommodate " "Device {device} in rack {rack} does not have sufficient space to accommodate "
"a height of {height}U" "a height of {height}U"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:322 #: netbox/dcim/models/devices.py:323
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Unable to set 0U height: Found <a href=\"{url}\">{racked_instance_count} " "Unable to set 0U height: Found <a href=\"{url}\">{racked_instance_count} "
"instances</a> already mounted within racks." "instances</a> already mounted within racks."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:331 #: netbox/dcim/models/devices.py:332
msgid "" msgid ""
"Must delete all device bay templates associated with this device before " "Must delete all device bay templates associated with this device before "
"declassifying it as a parent device." "declassifying it as a parent device."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:337 #: netbox/dcim/models/devices.py:338
msgid "Child device types must be 0U." msgid "Child device types must be 0U."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:412 #: netbox/dcim/models/devices.py:413
msgid "module type" msgid "module type"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:413 #: netbox/dcim/models/devices.py:414
msgid "module types" msgid "module types"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:483 #: netbox/dcim/models/devices.py:484
msgid "Virtual machines may be assigned to this role" msgid "Virtual machines may be assigned to this role"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:495 #: netbox/dcim/models/devices.py:496
msgid "device role" msgid "device role"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:496 #: netbox/dcim/models/devices.py:497
msgid "device roles" msgid "device roles"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:510 #: netbox/dcim/models/devices.py:511
msgid "Optionally limit this platform to devices of a certain manufacturer" msgid "Optionally limit this platform to devices of a certain manufacturer"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:522 #: netbox/dcim/models/devices.py:523
msgid "platform" msgid "platform"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:523 #: netbox/dcim/models/devices.py:524
msgid "platforms" msgid "platforms"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:571 #: netbox/dcim/models/devices.py:572
msgid "The function this device serves" msgid "The function this device serves"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:598 #: netbox/dcim/models/devices.py:599
msgid "Chassis serial number, assigned by the manufacturer" msgid "Chassis serial number, assigned by the manufacturer"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:606 netbox/dcim/models/devices.py:1192 #: netbox/dcim/models/devices.py:607 netbox/dcim/models/devices.py:1193
msgid "A unique tag used to identify this device" msgid "A unique tag used to identify this device"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:633 #: netbox/dcim/models/devices.py:634
msgid "position (U)" msgid "position (U)"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:641 #: netbox/dcim/models/devices.py:642
msgid "rack face" msgid "rack face"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:662 netbox/dcim/models/devices.py:1419 #: netbox/dcim/models/devices.py:663 netbox/dcim/models/devices.py:1420
#: netbox/virtualization/models/virtualmachines.py:95 #: netbox/virtualization/models/virtualmachines.py:95
msgid "primary IPv4" msgid "primary IPv4"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:670 netbox/dcim/models/devices.py:1427 #: netbox/dcim/models/devices.py:671 netbox/dcim/models/devices.py:1428
#: netbox/virtualization/models/virtualmachines.py:103 #: netbox/virtualization/models/virtualmachines.py:103
msgid "primary IPv6" msgid "primary IPv6"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:678 #: netbox/dcim/models/devices.py:679
msgid "out-of-band IP" msgid "out-of-band IP"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:695 #: netbox/dcim/models/devices.py:696
msgid "VC position" msgid "VC position"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:698 #: netbox/dcim/models/devices.py:699
msgid "Virtual chassis position" msgid "Virtual chassis position"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:701 #: netbox/dcim/models/devices.py:702
msgid "VC priority" msgid "VC priority"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:705 #: netbox/dcim/models/devices.py:706
msgid "Virtual chassis master election priority" msgid "Virtual chassis master election priority"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:708 netbox/dcim/models/sites.py:208 #: netbox/dcim/models/devices.py:709 netbox/dcim/models/sites.py:208
msgid "latitude" msgid "latitude"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:713 netbox/dcim/models/devices.py:721 #: netbox/dcim/models/devices.py:714 netbox/dcim/models/devices.py:722
#: netbox/dcim/models/sites.py:213 netbox/dcim/models/sites.py:221 #: netbox/dcim/models/sites.py:213 netbox/dcim/models/sites.py:221
msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgid "GPS coordinate in decimal format (xx.yyyyyy)"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:716 netbox/dcim/models/sites.py:216 #: netbox/dcim/models/devices.py:717 netbox/dcim/models/sites.py:216
msgid "longitude" msgid "longitude"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:789 #: netbox/dcim/models/devices.py:790
msgid "Device name must be unique per site." msgid "Device name must be unique per site."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:800 netbox/ipam/models/services.py:71 #: netbox/dcim/models/devices.py:801 netbox/ipam/models/services.py:71
msgid "device" msgid "device"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:801 #: netbox/dcim/models/devices.py:802
msgid "devices" msgid "devices"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:824 #: netbox/dcim/models/devices.py:825
#, python-brace-format #, python-brace-format
msgid "Rack {rack} does not belong to site {site}." msgid "Rack {rack} does not belong to site {site}."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:829 #: netbox/dcim/models/devices.py:830
#, python-brace-format #, python-brace-format
msgid "Location {location} does not belong to site {site}." msgid "Location {location} does not belong to site {site}."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:835 #: netbox/dcim/models/devices.py:836
#, python-brace-format #, python-brace-format
msgid "Rack {rack} does not belong to location {location}." msgid "Rack {rack} does not belong to location {location}."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:842 #: netbox/dcim/models/devices.py:843
msgid "Cannot select a rack face without assigning a rack." msgid "Cannot select a rack face without assigning a rack."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:846 #: netbox/dcim/models/devices.py:847
msgid "Cannot select a rack position without assigning a rack." msgid "Cannot select a rack position without assigning a rack."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:852 #: netbox/dcim/models/devices.py:853
msgid "Position must be in increments of 0.5 rack units." msgid "Position must be in increments of 0.5 rack units."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:856 #: netbox/dcim/models/devices.py:857
msgid "Must specify rack face when defining rack position." msgid "Must specify rack face when defining rack position."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:864 #: netbox/dcim/models/devices.py:865
#, python-brace-format #, python-brace-format
msgid "A 0U device type ({device_type}) cannot be assigned to a rack position." msgid "A 0U device type ({device_type}) cannot be assigned to a rack position."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:875 #: netbox/dcim/models/devices.py:876
msgid "" msgid ""
"Child device types cannot be assigned to a rack face. This is an attribute " "Child device types cannot be assigned to a rack face. This is an attribute "
"of the parent device." "of the parent device."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:882 #: netbox/dcim/models/devices.py:883
msgid "" msgid ""
"Child device types cannot be assigned to a rack position. This is an " "Child device types cannot be assigned to a rack position. This is an "
"attribute of the parent device." "attribute of the parent device."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:896 #: netbox/dcim/models/devices.py:897
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"U{position} is already occupied or does not have sufficient space to " "U{position} is already occupied or does not have sufficient space to "
"accommodate this device type: {device_type} ({u_height}U)" "accommodate this device type: {device_type} ({u_height}U)"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:911 #: netbox/dcim/models/devices.py:912
#, python-brace-format #, python-brace-format
msgid "{ip} is not an IPv4 address." msgid "{ip} is not an IPv4 address."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:923 netbox/dcim/models/devices.py:941 #: netbox/dcim/models/devices.py:924 netbox/dcim/models/devices.py:942
#, python-brace-format #, python-brace-format
msgid "The specified IP address ({ip}) is not assigned to this device." msgid "The specified IP address ({ip}) is not assigned to this device."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:929 #: netbox/dcim/models/devices.py:930
#, python-brace-format #, python-brace-format
msgid "{ip} is not an IPv6 address." msgid "{ip} is not an IPv6 address."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:959 #: netbox/dcim/models/devices.py:960
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"The assigned platform is limited to {platform_manufacturer} device types, " "The assigned platform is limited to {platform_manufacturer} device types, "
"but this device's type belongs to {devicetype_manufacturer}." "but this device's type belongs to {devicetype_manufacturer}."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:970 #: netbox/dcim/models/devices.py:971
#, python-brace-format #, python-brace-format
msgid "The assigned cluster belongs to a different site ({site})" msgid "The assigned cluster belongs to a different site ({site})"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:977 #: netbox/dcim/models/devices.py:978
#, python-brace-format #, python-brace-format
msgid "The assigned cluster belongs to a different location ({location})" msgid "The assigned cluster belongs to a different location ({location})"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:985 #: netbox/dcim/models/devices.py:986
msgid "A device assigned to a virtual chassis must have its position defined." msgid "A device assigned to a virtual chassis must have its position defined."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:991 #: netbox/dcim/models/devices.py:992
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Device cannot be removed from virtual chassis {virtual_chassis} because it " "Device cannot be removed from virtual chassis {virtual_chassis} because it "
"is currently designated as its master." "is currently designated as its master."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1199 #: netbox/dcim/models/devices.py:1200
msgid "module" msgid "module"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1200 #: netbox/dcim/models/devices.py:1201
msgid "modules" msgid "modules"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1213 #: netbox/dcim/models/devices.py:1214
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Module must be installed within a module bay belonging to the assigned " "Module must be installed within a module bay belonging to the assigned "
"device ({device})." "device ({device})."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1340 #: netbox/dcim/models/devices.py:1341
msgid "domain" msgid "domain"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1353 netbox/dcim/models/devices.py:1354 #: netbox/dcim/models/devices.py:1354 netbox/dcim/models/devices.py:1355
msgid "virtual chassis" msgid "virtual chassis"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1366 #: netbox/dcim/models/devices.py:1367
#, python-brace-format #, python-brace-format
msgid "The selected master ({master}) is not assigned to this virtual chassis." msgid "The selected master ({master}) is not assigned to this virtual chassis."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1382 #: netbox/dcim/models/devices.py:1383
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"Unable to delete virtual chassis {self}. There are member interfaces which " "Unable to delete virtual chassis {self}. There are member interfaces which "
"form a cross-chassis LAG interfaces." "form a cross-chassis LAG interfaces."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1408 netbox/vpn/models/l2vpn.py:37 #: netbox/dcim/models/devices.py:1409 netbox/vpn/models/l2vpn.py:37
msgid "identifier" msgid "identifier"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1409 #: netbox/dcim/models/devices.py:1410
msgid "Numeric identifier unique to the parent device" msgid "Numeric identifier unique to the parent device"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1437 netbox/extras/models/customfields.py:225 #: netbox/dcim/models/devices.py:1438 netbox/extras/models/customfields.py:225
#: netbox/extras/models/models.py:107 netbox/extras/models/models.py:694 #: netbox/extras/models/models.py:107 netbox/extras/models/models.py:694
#: netbox/netbox/models/__init__.py:119 #: netbox/netbox/models/__init__.py:119
msgid "comments" msgid "comments"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1453 #: netbox/dcim/models/devices.py:1454
msgid "virtual device context" msgid "virtual device context"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1454 #: netbox/dcim/models/devices.py:1455
msgid "virtual device contexts" msgid "virtual device contexts"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1483 #: netbox/dcim/models/devices.py:1484
#, python-brace-format #, python-brace-format
msgid "{ip} is not an IPv{family} address." msgid "{ip} is not an IPv{family} address."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1489 #: netbox/dcim/models/devices.py:1490
msgid "Primary IP address must belong to an interface on the assigned device." msgid "Primary IP address must belong to an interface on the assigned device."
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1521 #: netbox/dcim/models/devices.py:1522
msgid "MAC addresses" msgid "MAC addresses"
msgstr "" msgstr ""
#: netbox/dcim/models/devices.py:1544
msgid ""
"Cannot unassign MAC Address while it is designated as the primary MAC for an "
"object"
msgstr ""
#: netbox/dcim/models/devices.py:1548
msgid ""
"Cannot reassign MAC Address while it is designated as the primary MAC for an "
"object"
msgstr ""
#: netbox/dcim/models/mixins.py:94 #: netbox/dcim/models/mixins.py:94
#, python-brace-format #, python-brace-format
msgid "Please select a {scope_type}." msgid "Please select a {scope_type}."

View File

@ -150,8 +150,8 @@ class ClusterAddDevicesForm(forms.Form):
for scope_field in ['site', 'location']: for scope_field in ['site', 'location']:
device_scope = getattr(device, scope_field) device_scope = getattr(device, scope_field)
if ( if (
self.cluster.scope_type.model_class() == apps.get_model('dcim', scope_field) self.cluster.scope_type.model_class() == apps.get_model('dcim', scope_field) and
and device_scope != self.cluster.scope device_scope != self.cluster.scope
): ):
raise ValidationError({ raise ValidationError({
'devices': _( 'devices': _(

View File

@ -15,7 +15,7 @@ def populate_denormalized_fields(apps, schema_editor):
cluster._site_id = cluster.site_id cluster._site_id = cluster.site_id
# Note: Location cannot be set prior to migration # Note: Location cannot be set prior to migration
Cluster.objects.bulk_update(clusters, ['_region', '_site_group', '_site']) Cluster.objects.bulk_update(clusters, ['_region', '_site_group', '_site'], batch_size=100)
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -120,9 +120,9 @@ class VMInterfaceTable(BaseInterfaceTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = VMInterface model = VMInterface
fields = ( fields = (
'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'tags', 'pk', 'id', 'name', 'virtual_machine', 'enabled', 'mtu', 'mode', 'description', 'tags', 'vrf',
'vrf', 'primary_mac_address', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'primary_mac_address', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
'tagged_vlans', 'qinq_svlan', 'created', 'last_updated', 'qinq_svlan', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description') default_columns = ('pk', 'name', 'virtual_machine', 'enabled', 'description')
@ -144,11 +144,11 @@ class VirtualMachineVMInterfaceTable(VMInterfaceTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = VMInterface model = VMInterface
fields = ( fields = (
'pk', 'id', 'name', 'enabled', 'parent', 'bridge', 'mac_address', 'mtu', 'mode', 'description', 'tags', 'pk', 'id', 'name', 'enabled', 'parent', 'bridge', 'primary_mac_address', 'mtu', 'mode', 'description',
'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'tags', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
'actions', 'qinq_svlan', 'actions',
) )
default_columns = ('pk', 'name', 'enabled', 'mac_address', 'mtu', 'mode', 'description', 'ip_addresses') default_columns = ('pk', 'name', 'enabled', 'primary_mac_address', 'mtu', 'mode', 'description', 'ip_addresses')
row_attrs = { row_attrs = {
'data-name': lambda record: record.name, 'data-name': lambda record: record.name,
'data-virtual': lambda record: "true", 'data-virtual': lambda record: "true",