mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-16 00:32:18 -06:00
Compare commits
10 Commits
main
...
20911-drop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5359ae4fc2 | ||
|
|
05619a9745 | ||
|
|
6317bcc657 | ||
|
|
20f52153a4 | ||
|
|
5a1282e326 | ||
|
|
cb13eb277f | ||
|
|
24642be351 | ||
|
|
89af9efd85 | ||
|
|
99d678502f | ||
|
|
e6300ee06d |
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -30,13 +30,13 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v4
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
build-mode: ${{ matrix.build-mode }}
|
build-mode: ${{ matrix.build-mode }}
|
||||||
config-file: .github/codeql/codeql-config.yml
|
config-file: .github/codeql/codeql-config.yml
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v4
|
uses: github/codeql-action/analyze@v3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
|||||||
@@ -44,4 +44,3 @@ class DataFileSerializer(NetBoxModelSerializer):
|
|||||||
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
|
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
|
||||||
]
|
]
|
||||||
brief_fields = ('id', 'url', 'display', 'path')
|
brief_fields = ('id', 'url', 'display', 'path')
|
||||||
read_only_fields = ['path', 'last_updated', 'size', 'hash']
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from django.core.validators import RegexValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
|
from netbox.constants import CENSOR_TOKEN, CENSOR_TOKEN_CHANGED
|
||||||
from netbox.models import PrimaryModel
|
from netbox.models import PrimaryModel
|
||||||
@@ -128,9 +128,7 @@ class DataSource(JobsMixin, PrimaryModel):
|
|||||||
# Ensure URL scheme matches selected type
|
# Ensure URL scheme matches selected type
|
||||||
if self.backend_class.is_local and self.url_scheme not in ('file', ''):
|
if self.backend_class.is_local and self.url_scheme not in ('file', ''):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'source_url': _("URLs for local sources must start with {scheme} (or specify no scheme)").format(
|
'source_url': "URLs for local sources must start with file:// (or specify no scheme)"
|
||||||
scheme='file://'
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -733,9 +733,10 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm):
|
|||||||
)
|
)
|
||||||
module_bay = DynamicModelChoiceField(
|
module_bay = DynamicModelChoiceField(
|
||||||
label=_('Module bay'),
|
label=_('Module bay'),
|
||||||
queryset=ModuleBay.objects.all(),
|
queryset=ModuleBay.objects.order_by('name'),
|
||||||
query_params={
|
query_params={
|
||||||
'device_id': '$device'
|
'device_id': '$device',
|
||||||
|
'ordering': 'name',
|
||||||
},
|
},
|
||||||
context={
|
context={
|
||||||
'disabled': 'installed_module',
|
'disabled': 'installed_module',
|
||||||
|
|||||||
@@ -211,16 +211,12 @@ def sync_cached_scope_fields(instance, created, **kwargs):
|
|||||||
for model in (Prefix, Cluster, WirelessLAN):
|
for model in (Prefix, Cluster, WirelessLAN):
|
||||||
qs = model.objects.filter(**filters)
|
qs = model.objects.filter(**filters)
|
||||||
|
|
||||||
# Bulk update cached fields to avoid O(N) performance issues with large datasets.
|
|
||||||
# This does not trigger post_save signals, avoiding spurious change log entries.
|
|
||||||
objects_to_update = []
|
|
||||||
for obj in qs:
|
for obj in qs:
|
||||||
# Recompute cache using the same logic as save()
|
# Recompute cache using the same logic as save()
|
||||||
obj.cache_related_objects()
|
obj.cache_related_objects()
|
||||||
objects_to_update.append(obj)
|
obj.save(update_fields=[
|
||||||
|
'_location',
|
||||||
if objects_to_update:
|
'_site',
|
||||||
model.objects.bulk_update(
|
'_site_group',
|
||||||
objects_to_update,
|
'_region',
|
||||||
['_location', '_site', '_site_group', '_region']
|
])
|
||||||
)
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class ConfigContextProfileSerializer(PrimaryModelSerializer):
|
|||||||
)
|
)
|
||||||
data_file = DataFileSerializer(
|
data_file = DataFileSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False
|
read_only=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -143,7 +143,7 @@ class ConfigContextSerializer(OwnerMixin, ChangeLogMessageSerializer, ValidatedM
|
|||||||
)
|
)
|
||||||
data_file = DataFileSerializer(
|
data_file = DataFileSerializer(
|
||||||
nested=True,
|
nested=True,
|
||||||
required=False
|
read_only=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@@ -8,7 +7,7 @@ from rest_framework import status
|
|||||||
|
|
||||||
from core.choices import ManagedFileRootPathChoices
|
from core.choices import ManagedFileRootPathChoices
|
||||||
from core.events import *
|
from core.events import *
|
||||||
from core.models import DataFile, DataSource, ObjectType
|
from core.models import ObjectType
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
from extras.models import *
|
from extras.models import *
|
||||||
@@ -732,51 +731,6 @@ class ConfigContextProfileTest(APIViewTestCases.APIViewTestCase):
|
|||||||
)
|
)
|
||||||
ConfigContextProfile.objects.bulk_create(profiles)
|
ConfigContextProfile.objects.bulk_create(profiles)
|
||||||
|
|
||||||
def test_update_data_source_and_data_file(self):
|
|
||||||
"""
|
|
||||||
Regression test: Ensure data_source and data_file can be assigned via the API.
|
|
||||||
|
|
||||||
This specifically covers PATCHing a ConfigContext with integer IDs for both fields.
|
|
||||||
"""
|
|
||||||
self.add_permissions(
|
|
||||||
'core.view_datafile',
|
|
||||||
'core.view_datasource',
|
|
||||||
'extras.view_configcontextprofile',
|
|
||||||
'extras.change_configcontextprofile',
|
|
||||||
)
|
|
||||||
config_context_profile = ConfigContextProfile.objects.first()
|
|
||||||
|
|
||||||
# Create a data source and file
|
|
||||||
datasource = DataSource.objects.create(
|
|
||||||
name='Data Source 1',
|
|
||||||
type='local',
|
|
||||||
source_url='file:///tmp/netbox-datasource/',
|
|
||||||
)
|
|
||||||
# Generate a valid dummy YAML file
|
|
||||||
file_data = b'profile: configcontext\n'
|
|
||||||
datafile = DataFile.objects.create(
|
|
||||||
source=datasource,
|
|
||||||
path='dir1/file1.yml',
|
|
||||||
last_updated=now(),
|
|
||||||
size=len(file_data),
|
|
||||||
hash=hashlib.sha256(file_data).hexdigest(),
|
|
||||||
data=file_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
url = self._get_detail_url(config_context_profile)
|
|
||||||
payload = {
|
|
||||||
'data_source': datasource.pk,
|
|
||||||
'data_file': datafile.pk,
|
|
||||||
}
|
|
||||||
response = self.client.patch(url, payload, format='json', **self.header)
|
|
||||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
config_context_profile.refresh_from_db()
|
|
||||||
self.assertEqual(config_context_profile.data_source_id, datasource.pk)
|
|
||||||
self.assertEqual(config_context_profile.data_file_id, datafile.pk)
|
|
||||||
self.assertEqual(response.data['data_source']['id'], datasource.pk)
|
|
||||||
self.assertEqual(response.data['data_file']['id'], datafile.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = ConfigContext
|
model = ConfigContext
|
||||||
@@ -858,51 +812,6 @@ class ConfigContextTest(APIViewTestCases.APIViewTestCase):
|
|||||||
rendered_context = device.get_config_context()
|
rendered_context = device.get_config_context()
|
||||||
self.assertEqual(rendered_context['bar'], 456)
|
self.assertEqual(rendered_context['bar'], 456)
|
||||||
|
|
||||||
def test_update_data_source_and_data_file(self):
|
|
||||||
"""
|
|
||||||
Regression test: Ensure data_source and data_file can be assigned via the API.
|
|
||||||
|
|
||||||
This specifically covers PATCHing a ConfigContext with integer IDs for both fields.
|
|
||||||
"""
|
|
||||||
self.add_permissions(
|
|
||||||
'core.view_datafile',
|
|
||||||
'core.view_datasource',
|
|
||||||
'extras.view_configcontext',
|
|
||||||
'extras.change_configcontext',
|
|
||||||
)
|
|
||||||
config_context = ConfigContext.objects.first()
|
|
||||||
|
|
||||||
# Create a data source and file
|
|
||||||
datasource = DataSource.objects.create(
|
|
||||||
name='Data Source 1',
|
|
||||||
type='local',
|
|
||||||
source_url='file:///tmp/netbox-datasource/',
|
|
||||||
)
|
|
||||||
# Generate a valid dummy YAML file
|
|
||||||
file_data = b'context: config\n'
|
|
||||||
datafile = DataFile.objects.create(
|
|
||||||
source=datasource,
|
|
||||||
path='dir1/file1.yml',
|
|
||||||
last_updated=now(),
|
|
||||||
size=len(file_data),
|
|
||||||
hash=hashlib.sha256(file_data).hexdigest(),
|
|
||||||
data=file_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
url = self._get_detail_url(config_context)
|
|
||||||
payload = {
|
|
||||||
'data_source': datasource.pk,
|
|
||||||
'data_file': datafile.pk,
|
|
||||||
}
|
|
||||||
response = self.client.patch(url, payload, format='json', **self.header)
|
|
||||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
config_context.refresh_from_db()
|
|
||||||
self.assertEqual(config_context.data_source_id, datasource.pk)
|
|
||||||
self.assertEqual(config_context.data_file_id, datafile.pk)
|
|
||||||
self.assertEqual(response.data['data_source']['id'], datasource.pk)
|
|
||||||
self.assertEqual(response.data['data_file']['id'], datafile.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
|
class ConfigTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = ConfigTemplate
|
model = ConfigTemplate
|
||||||
|
|||||||
@@ -372,8 +372,8 @@ class IPAddressForm(TenancyForm, PrimaryModelForm):
|
|||||||
'virtual_machine_id': instance.assigned_object.virtual_machine.pk,
|
'virtual_machine_id': instance.assigned_object.virtual_machine.pk,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Disable object assignment fields if the IP address is designated as primary or OOB
|
# Disable object assignment fields if the IP address is designated as primary
|
||||||
if self.initial.get('primary_for_parent') or self.initial.get('oob_for_parent'):
|
if self.initial.get('primary_for_parent'):
|
||||||
self.fields['interface'].disabled = True
|
self.fields['interface'].disabled = True
|
||||||
self.fields['vminterface'].disabled = True
|
self.fields['vminterface'].disabled = True
|
||||||
self.fields['fhrpgroup'].disabled = True
|
self.fields['fhrpgroup'].disabled = True
|
||||||
|
|||||||
@@ -940,13 +940,6 @@ class IPAddress(ContactsMixin, PrimaryModel):
|
|||||||
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
|
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
|
||||||
)
|
)
|
||||||
|
|
||||||
# can't use is_oob_ip as self.assigned_object might be changed
|
|
||||||
if hasattr(original_parent, 'oob_ip') and original_parent.oob_ip_id == self.pk:
|
|
||||||
if parent != original_parent:
|
|
||||||
raise ValidationError(
|
|
||||||
_("Cannot reassign IP address while it is designated as the OOB IP for the parent object")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Validate IP status selection
|
# Validate IP status selection
|
||||||
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
|
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
import strawberry_django
|
import strawberry_django
|
||||||
from strawberry import ID
|
from strawberry import ID
|
||||||
from strawberry_django import ComparisonFilterLookup, FilterLookup
|
from strawberry_django import FilterLookup
|
||||||
|
|
||||||
from core.graphql.filter_mixins import ChangeLoggingMixin
|
from core.graphql.filter_mixins import ChangeLoggingMixin
|
||||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
||||||
@@ -23,7 +23,7 @@ __all__ = (
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BaseModelFilter:
|
class BaseModelFilter:
|
||||||
id: ComparisonFilterLookup[ID] | None = strawberry_django.filter_field()
|
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||||
|
|
||||||
|
|
||||||
class ChangeLoggedModelFilter(ChangeLoggingMixin, BaseModelFilter):
|
class ChangeLoggedModelFilter(ChangeLoggingMixin, BaseModelFilter):
|
||||||
|
|||||||
2
netbox/project-static/dist/netbox.js
vendored
2
netbox/project-static/dist/netbox.js
vendored
File diff suppressed because one or more lines are too long
6
netbox/project-static/dist/netbox.js.map
vendored
6
netbox/project-static/dist/netbox.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -75,10 +75,16 @@ export class DynamicTomSelect extends TomSelect {
|
|||||||
load(value: string) {
|
load(value: string) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
// Save current selection before clearing
|
||||||
|
const currentValue = self.getValue();
|
||||||
|
|
||||||
// Automatically clear any cached options. (Only options included
|
// Automatically clear any cached options. (Only options included
|
||||||
// in the API response should be present.)
|
// in the API response should be present.)
|
||||||
self.clearOptions();
|
self.clearOptions();
|
||||||
|
|
||||||
|
// Clear user_options to prevent the pre-selected option from being treated specially
|
||||||
|
(self as any).user_options = {};
|
||||||
|
|
||||||
// Populate the null option (if any) if not searching
|
// Populate the null option (if any) if not searching
|
||||||
if (self.nullOption && !value) {
|
if (self.nullOption && !value) {
|
||||||
self.addOption(self.nullOption);
|
self.addOption(self.nullOption);
|
||||||
@@ -98,16 +104,29 @@ export class DynamicTomSelect extends TomSelect {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(apiData => {
|
.then(apiData => {
|
||||||
const results: Dict[] = apiData.results;
|
const results: Dict[] = apiData.results;
|
||||||
const options: Dict[] = [];
|
|
||||||
for (const result of results) {
|
// Add options and manually set $order to ensure correct sorting
|
||||||
|
results.forEach((result, index) => {
|
||||||
const option = self.getOptionFromData(result);
|
const option = self.getOptionFromData(result);
|
||||||
options.push(option);
|
self.addOption(option);
|
||||||
|
// Set $order after addOption() to override any special handling of pre-selected items
|
||||||
|
const key = option[self.settings.valueField as string] as string;
|
||||||
|
if (self.options[key]) {
|
||||||
|
(self.options[key] as any).$order = index;
|
||||||
}
|
}
|
||||||
return options;
|
});
|
||||||
})
|
|
||||||
// Pass the options to the callback function
|
self.loading--;
|
||||||
.then(options => {
|
if (self.loading === 0) {
|
||||||
self.loadCallback(options, []);
|
self.wrapper.classList.remove(self.settings.loadingClass as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the current selection
|
||||||
|
if (currentValue && !self.items.includes(currentValue as string)) {
|
||||||
|
self.items.push(currentValue as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.refreshOptions(false);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
self.loadCallback([], []);
|
self.loadCallback([], []);
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ export function initDynamicSelects(): void {
|
|||||||
labelField: LABEL_FIELD,
|
labelField: LABEL_FIELD,
|
||||||
maxOptions: MAX_OPTIONS,
|
maxOptions: MAX_OPTIONS,
|
||||||
|
|
||||||
|
// Preserve API response order
|
||||||
|
sortField: '$order',
|
||||||
|
|
||||||
// Disable local search (search is performed on the backend)
|
// Disable local search (search is performed on the backend)
|
||||||
searchField: [],
|
searchField: [],
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-01-16 05:04+0000\n"
|
"POT-Creation-Date: 2026-01-13 05:05+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"
|
||||||
@@ -2435,7 +2435,7 @@ msgstr ""
|
|||||||
msgid "Change logging is not supported for this object type ({type})."
|
msgid "Change logging is not supported for this object type ({type})."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/config.py:21 netbox/core/models/data.py:284
|
#: netbox/core/models/config.py:21 netbox/core/models/data.py:282
|
||||||
#: netbox/core/models/files.py:29 netbox/core/models/jobs.py:60
|
#: netbox/core/models/files.py:29 netbox/core/models/jobs.py:60
|
||||||
#: netbox/extras/models/models.py:847 netbox/extras/models/notifications.py:39
|
#: netbox/extras/models/models.py:847 netbox/extras/models/notifications.py:39
|
||||||
#: netbox/extras/models/notifications.py:195
|
#: netbox/extras/models/notifications.py:195
|
||||||
@@ -2541,63 +2541,58 @@ msgstr ""
|
|||||||
msgid "Unknown backend type: {type}"
|
msgid "Unknown backend type: {type}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:131
|
#: netbox/core/models/data.py:180
|
||||||
#, python-brace-format
|
|
||||||
msgid "URLs for local sources must start with {scheme} (or specify no scheme)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: netbox/core/models/data.py:182
|
|
||||||
msgid "Cannot initiate sync; syncing already in progress."
|
msgid "Cannot initiate sync; syncing already in progress."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:195
|
#: netbox/core/models/data.py:193
|
||||||
msgid ""
|
msgid ""
|
||||||
"There was an error initializing the backend. A dependency needs to be "
|
"There was an error initializing the backend. A dependency needs to be "
|
||||||
"installed: "
|
"installed: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:288 netbox/core/models/files.py:33
|
#: netbox/core/models/data.py:286 netbox/core/models/files.py:33
|
||||||
#: netbox/netbox/models/features.py:67
|
#: netbox/netbox/models/features.py:67
|
||||||
msgid "last updated"
|
msgid "last updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:298 netbox/dcim/models/cables.py:622
|
#: netbox/core/models/data.py:296 netbox/dcim/models/cables.py:622
|
||||||
msgid "path"
|
msgid "path"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:301
|
#: netbox/core/models/data.py:299
|
||||||
msgid "File path relative to the data source's root"
|
msgid "File path relative to the data source's root"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:305 netbox/ipam/models/ip.py:507
|
#: netbox/core/models/data.py:303 netbox/ipam/models/ip.py:507
|
||||||
msgid "size"
|
msgid "size"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:308
|
#: netbox/core/models/data.py:306
|
||||||
msgid "hash"
|
msgid "hash"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:312
|
#: netbox/core/models/data.py:310
|
||||||
msgid "Length must be 64 hexadecimal characters."
|
msgid "Length must be 64 hexadecimal characters."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:314
|
#: netbox/core/models/data.py:312
|
||||||
msgid "SHA256 hash of the file data"
|
msgid "SHA256 hash of the file data"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:328
|
#: netbox/core/models/data.py:326
|
||||||
msgid "data file"
|
msgid "data file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:329
|
#: netbox/core/models/data.py:327
|
||||||
msgid "data files"
|
msgid "data files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:402
|
#: netbox/core/models/data.py:400
|
||||||
msgid "auto sync record"
|
msgid "auto sync record"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/models/data.py:403
|
#: netbox/core/models/data.py:401
|
||||||
msgid "auto sync records"
|
msgid "auto sync records"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -11245,13 +11240,7 @@ msgid ""
|
|||||||
"parent object"
|
"parent object"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/ipam/models/ip.py:947
|
#: netbox/ipam/models/ip.py:946
|
||||||
msgid ""
|
|
||||||
"Cannot reassign IP address while it is designated as the OOB IP for the "
|
|
||||||
"parent object"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: netbox/ipam/models/ip.py:953
|
|
||||||
msgid "Only IPv6 addresses can be assigned SLAAC status"
|
msgid "Only IPv6 addresses can be assigned SLAAC status"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -12500,8 +12489,8 @@ msgstr ""
|
|||||||
msgid "Delete Selected"
|
msgid "Delete Selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/netbox/plugins/navigation.py:53
|
#: netbox/netbox/plugins/navigation.py:55
|
||||||
#: netbox/netbox/plugins/navigation.py:89
|
#: netbox/netbox/plugins/navigation.py:88
|
||||||
msgid "Permissions must be passed as a tuple or list."
|
msgid "Permissions must be passed as a tuple or list."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -12509,7 +12498,7 @@ msgstr ""
|
|||||||
msgid "Buttons must be passed as a tuple or list."
|
msgid "Buttons must be passed as a tuple or list."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/netbox/plugins/navigation.py:95
|
#: netbox/netbox/plugins/navigation.py:92
|
||||||
msgid "Button color must be a choice within ButtonColorChoices."
|
msgid "Button color must be a choice within ButtonColorChoices."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user