Compare commits

..

30 Commits

Author SHA1 Message Date
Jeremy Stretch
f845b2cf07 Release v4.2.2 2025-01-17 15:05:09 -05:00
atownson
2ed4a2b005 Fixes: #18369 - Remove the json filter for protection rules (#18388)
* Remove the json filter for protection rules

* Configure PROTECTION_RULE config attribute to use ConfigJSONEncoder as serializer

* Tweak getattr()

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2025-01-17 14:02:12 -05:00
Jeremy Stretch
5b9210dfa5 Fixes #18392: Exclude config contexts assigned to locations for VMs 2025-01-17 13:14:05 -05:00
Jeremy Stretch
4a13664e0f Closes #18425: Remove the triage priority field from GitHub issue templates 2025-01-17 11:06:17 -05:00
Jeremy Stretch
a9f3c74b0c Fixes #18379: Ensure RSS feed content within dashboard widget is sanitized 2025-01-17 10:25:22 -05:00
Brian Tiemann
50b7f46fc0 Migrate DEFAULT_FILE_STORAGE to STORAGES 2025-01-17 09:04:51 -05:00
Brian Tiemann
07ad4c1321 Make GFK scope field sortable=False on tables where it appears 2025-01-17 08:52:12 -05:00
bctiemann
4a1fea3504 Fixes: #18336 - Perform Rack object validation of u_height and starting_unit on rack_type if present (#18395)
* Perform Rack object validation of u_height and starting_unit on rack_type if present

* Calculate effective values before doing validation
2025-01-17 08:45:17 -05:00
bctiemann
993d8f1480 Fixes: #18373 - Fix validation of site in Assign Device to Cluster flow (#18375)
* Fix validation of site in Assign Device to Cluster flow

* Validate Location as well as Site scope
2025-01-17 08:35:17 -05:00
bctiemann
c3efa2149c Fixes: #18350 - Remove 'site' and 'provider_network' from CircuitTerminationIndex.display_attrs (#18351)
* Remove 'site' and 'provider_network' from CircuitTerminationIndex.display_attrs

* Use '_site' and '_provider_network' in display_attrs

* Replace private fields with 'termination'
2025-01-17 08:28:43 -05:00
Jeremy Stretch
a75fa53d4d Closes #18348: Disable legacy pre-commit hook script 2025-01-13 08:50:34 -05:00
Jeremy Stretch
e75d327f38 Fixes #18376: Include tagged VLANs in interfaces list for Q-in-Q interfaces 2025-01-10 09:10:34 -05:00
github-actions
a79d869bd8 Update source translation strings 2025-01-10 05:02:08 +00:00
Brian Tiemann
32422d1683 Don't cache CACHE_KEY_CATALOG_ERROR if ISOLATED_DEPLOYMENT is True 2025-01-09 15:21:27 -05:00
Jeremy Stretch
571f604ce8 Fixes #18368: Restore missing fields on REST API serializer for MAC addresses 2025-01-09 14:53:03 -05:00
Jeremy Stretch
b12c8c880f Fixes #18363: Fix assignment of MAC addresses to interfaces via REST API (#18367)
* Fixes #18363: Fix assignment of MAC addresses to interfaces via REST API

* Add missing API & view tests
2025-01-09 13:55:19 -05:00
Jeremy Stretch
b11f179527 Closes #18362: Create a system job for census reporting 2025-01-09 11:56:09 -05:00
Brian Tiemann
80e1fd02bb Update docs to indicate PostgreSQL 13+ requirement 2025-01-09 10:58:51 -05:00
github-actions
4090afbf24 Update source translation strings 2025-01-09 05:02:09 +00:00
Jeremy Stretch
d04fc11c61 Release v4.2.1 (#18346)
* Release v4.2.1

* Add changelog for #18282
2025-01-08 10:19:28 -05:00
Brian Tiemann
f6b8c1966d Use order_by to change ordering behavior of VLAN column rather than changing accessor 2025-01-08 09:54:00 -05:00
Brian Tiemann
4456c488f1 Change PrefixTable.vlan to represent the VLAN ID rather than the VLAN object, to enable more useful sorting by VLAN ID rather than site-grouped VLAN objects 2025-01-08 09:54:00 -05:00
Jeremy Stretch
53aa2c8624 Fixes #18329: Pin strawberry-graphql-django to v0.52.0 to resolve upstream bug 2025-01-08 08:59:54 -05:00
github-actions
ffac0974dd Update source translation strings 2025-01-08 05:02:12 +00:00
bctiemann
e518f08604 Fixes: #18316 - Fix PrefixIndex reference to 'site' (#18322)
* Fix PrefixIndex reference to 'site'

* Fix ClusterIndex reference to 'site' and add 'scope' to WirelessLANIndex
2025-01-07 10:47:05 -05:00
Tobias Genannt
4ae5529362 Fix #18314: Use get to avoid KeyError 2025-01-07 10:39:55 -05:00
Jeremy Stretch
ef6c89ee5d Fixes #18324: Correct filter names for certain related object listings 2025-01-07 10:34:35 -05:00
Jeremy Stretch
9c960c2387 Fixes #18318: Correct navigation breadcrumbs for module type UI view 2025-01-07 10:28:22 -05:00
github-actions
ed541220e8 Update source translation strings 2025-01-07 05:02:25 +00:00
Jeremy Stretch
14cec518f5 Closes #18311: Update minimum required version of PostgreSQL 2025-01-06 17:04:13 -05:00
45 changed files with 4093 additions and 3393 deletions

View File

@@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.2.0 placeholder: v4.2.2
validations: validations:
required: true required: true
- type: dropdown - type: dropdown
@@ -27,19 +27,6 @@ body:
- Other - Other
validations: validations:
required: true required: true
- type: dropdown
attributes:
label: Triage priority
description: >
Issue triage may be prioritized in some cases. Select whichever of the following
conditions applies, if any.
options:
- I volunteer to perform this work (if approved)
- I'm a NetBox Labs customer
- N/A
default: 2
validations:
required: true
- type: textarea - type: textarea
attributes: attributes:
label: Proposed functionality label: Proposed functionality

View File

@@ -22,24 +22,11 @@ body:
- Self-hosted - Self-hosted
validations: validations:
required: true required: true
- type: dropdown
attributes:
label: Triage priority
description: >
Issue triage may be prioritized in some cases. Select whichever of the following
conditions applies, if any.
options:
- I volunteer to perform this work (if approved)
- I'm a NetBox Labs customer
- N/A
default: 2
validations:
required: true
- type: input - type: input
attributes: attributes:
label: NetBox Version label: NetBox Version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.2.0 placeholder: v4.2.2
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@@ -8,8 +8,6 @@ django-cors-headers
# Runtime UI tool for debugging Django # Runtime UI tool for debugging Django
# https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst # https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst
# Pinned for DNS looukp bug; see https://github.com/netbox-community/netbox/issues/16454
# and https://github.com/jazzband/django-debug-toolbar/issues/1927
django-debug-toolbar django-debug-toolbar
# Library for writing reusable URL query filters # Library for writing reusable URL query filters
@@ -134,7 +132,8 @@ strawberry-graphql
# Strawberry GraphQL Django extension # Strawberry GraphQL Django extension
# https://github.com/strawberry-graphql/strawberry-django/releases # https://github.com/strawberry-graphql/strawberry-django/releases
strawberry-graphql-django # Pinned to v0.52.0 for suspected upstream bug; see #18329
strawberry-graphql-django==0.52.0
# SVG image rendering (used for rack elevations) # SVG image rendering (used for rack elevations)
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst # https://github.com/mozman/svgwrite/blob/master/NEWS.rst

View File

@@ -25,7 +25,7 @@ ALLOWED_HOSTS = ['*']
## DATABASE ## DATABASE
NetBox requires access to a PostgreSQL 12 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary: NetBox requires access to a PostgreSQL 13 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
* `NAME` - Database name * `NAME` - Database name
* `USER` - PostgreSQL username * `USER` - PostgreSQL username

View File

@@ -2,8 +2,8 @@
This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md). This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md).
!!! warning "PostgreSQL 12 or later required" !!! warning "PostgreSQL 13 or later required"
NetBox requires PostgreSQL 12 or later. Please note that MySQL and other relational databases are **not** supported. NetBox requires PostgreSQL 13 or later. Please note that MySQL and other relational databases are **not** supported.
## Installation ## Installation
@@ -34,7 +34,7 @@ This section entails the installation and configuration of a local PostgreSQL da
sudo systemctl enable --now postgresql sudo systemctl enable --now postgresql
``` ```
Before continuing, verify that you have installed PostgreSQL 12 or later: Before continuing, verify that you have installed PostgreSQL 13 or later:
```no-highlight ```no-highlight
psql -V psql -V

View File

@@ -21,7 +21,7 @@ The following sections detail how to set up a new instance of NetBox:
| Dependency | Supported Versions | | Dependency | Supported Versions |
|------------|--------------------| |------------|--------------------|
| Python | 3.10, 3.11, 3.12 | | Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 12+ | | PostgreSQL | 13+ |
| Redis | 4.0+ | | Redis | 4.0+ |
Below is a simplified overview of the NetBox application stack for reference: Below is a simplified overview of the NetBox application stack for reference:

View File

@@ -20,7 +20,7 @@ NetBox requires the following dependencies:
| Dependency | Supported Versions | | Dependency | Supported Versions |
|------------|--------------------| |------------|--------------------|
| Python | 3.10, 3.11, 3.12 | | Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 12+ | | PostgreSQL | 13+ |
| Redis | 4.0+ | | Redis | 4.0+ |
## 3. Install the Latest Release ## 3. Install the Latest Release

View File

@@ -79,5 +79,5 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and
| HTTP service | nginx or Apache | | HTTP service | nginx or Apache |
| WSGI service | gunicorn or uWSGI | | WSGI service | gunicorn or uWSGI |
| Application | Django/Python | | Application | Django/Python |
| Database | PostgreSQL 12+ | | Database | PostgreSQL 13+ |
| Task queuing | Redis/django-rq | | Task queuing | Redis/django-rq |

View File

@@ -1,10 +1,44 @@
# NetBox v4.2 # NetBox v4.2
## v4.2.2 (2025-01-17)
### Bug Fixes
* [#18336](https://github.com/netbox-community/netbox/issues/18336) - Validate new rack height against installed devices when changing a rack's type
* [#18350](https://github.com/netbox-community/netbox/issues/18350) - Fix `FieldDoesNotExist` exception when global search results include a circuit termination
* [#18353](https://github.com/netbox-community/netbox/issues/18353) - Disable fetching of plugin catalog data when `ISOLATED_DEPLOYMENT` is enabled
* [#18362](https://github.com/netbox-community/netbox/issues/18362) - Avoid transmitting census data on every worker restart
* [#18363](https://github.com/netbox-community/netbox/issues/18363) - Fix support for assigning a MAC address to an interface via the REST API
* [#18368](https://github.com/netbox-community/netbox/issues/18368) - Restore missing attributes from REST API serializer for MAC addresses (`tags`, `created`, `last_updated`, and custom fields)
* [#18369](https://github.com/netbox-community/netbox/issues/18369) - Fix `TypeError` exception when rendering the system configuration view with one or more custom classes defined under `PROTECTION_RULES`
* [#18373](https://github.com/netbox-community/netbox/issues/18373) - Fix `AttributeError` exception when attempting to assign host devices to a cluster
* [#18376](https://github.com/netbox-community/netbox/issues/18376) - Fix the display of tagged VLANs in interfaces list for Q-in-Q interfaces
* [#18379](https://github.com/netbox-community/netbox/issues/18379) - Ensure RSS feed dashboard widget content is sanitized
* [#18392](https://github.com/netbox-community/netbox/issues/18392) - Virtual machines should not inherit config contexts assigned to locations
* [#18400](https://github.com/netbox-community/netbox/issues/18400) - Fix support for `STORAGE_BACKEND` configuration parameter
* [#18406](https://github.com/netbox-community/netbox/issues/18406) - Scope column headers in object lists should not be orderable
---
## v4.2.1 (2025-01-08)
### Bug Fixes
* [#18282](https://github.com/netbox-community/netbox/issues/18282) - Fix ordering of prefixes list by assigned VLAN
* [#18314](https://github.com/netbox-community/netbox/issues/18314) - Fix KeyError exception when rendering pre-saved dashboard (`requires_internet` missing)
* [#18316](https://github.com/netbox-community/netbox/issues/18316) - Fix AttributeError exception when global search results include prefixes and/or clusters
* [#18318](https://github.com/netbox-community/netbox/issues/18318) - Correct navigation breadcrumbs for module type UI view
* [#18324](https://github.com/netbox-community/netbox/issues/18324) - Correct filtering for certain related object listings
* [#18329](https://github.com/netbox-community/netbox/issues/18329) - Address upstream bug in GraphQL API where only one primary IP address is returned within a device/VM list
---
## v4.2.0 (2025-01-06) ## v4.2.0 (2025-01-06)
### Breaking Changes ### Breaking Changes
* Support for the Django admin UI has been completely removed. (The Django admin UI was disabled by default in NetBox v4.0.) * Support for the Django admin UI has been completely removed. (The Django admin UI was disabled by default in NetBox v4.0.)
* This release drops support for PostgreSQL 12. PostgreSQL 13 or later is required to run this release.
* NetBox has adopted collation-based natural ordering for many models. This may alter the order in which some objects are listed by default. * NetBox has adopted collation-based natural ordering for many models. This may alter the order in which some objects are listed by default.
* Automatic redirects from pre-v4.1 UI views for virtual disks have been removed. * Automatic redirects from pre-v4.1 UI views for virtual disks have been removed.
* The `site` and `provider_network` foreign key fields on `circuits.CircuitTermination` have been replaced by the `termination` generic foreign key. * The `site` and `provider_network` foreign key fields on `circuits.CircuitTermination` have been replaced by the `termination` generic foreign key.

View File

@@ -34,7 +34,7 @@ class CircuitTerminationIndex(SearchIndex):
('port_speed', 2000), ('port_speed', 2000),
('upstream_speed', 2000), ('upstream_speed', 2000),
) )
display_attrs = ('circuit', 'site', 'provider_network', 'description') display_attrs = ('circuit', 'termination', 'description')
@register_search @register_search

View File

@@ -1,8 +1,11 @@
import logging import logging
import requests
import sys
from netbox.jobs import JobRunner from django.conf import settings
from netbox.jobs import JobRunner, system_job
from netbox.search.backends import search_backend from netbox.search.backends import search_backend
from .choices import DataSourceStatusChoices from .choices import DataSourceStatusChoices, JobIntervalChoices
from .exceptions import SyncError from .exceptions import SyncError
from .models import DataSource from .models import DataSource
@@ -31,3 +34,44 @@ class SyncDataSourceJob(JobRunner):
if type(e) is SyncError: if type(e) is SyncError:
logging.error(e) logging.error(e)
raise e raise e
@system_job(interval=JobIntervalChoices.INTERVAL_DAILY)
class SystemHousekeepingJob(JobRunner):
"""
Perform daily system housekeeping functions.
"""
class Meta:
name = "System Housekeeping"
def run(self, *args, **kwargs):
# Skip if running in development or test mode
if settings.DEBUG or 'test' in sys.argv:
return
# TODO: Migrate other housekeeping functions from the `housekeeping` management command.
self.send_census_report()
@staticmethod
def send_census_report():
"""
Send a census report (if enabled).
"""
# Skip if census reporting is disabled
if settings.ISOLATED_DEPLOYMENT or not settings.CENSUS_REPORTING_ENABLED:
return
census_data = {
'version': settings.RELEASE.full_version,
'python_version': sys.version.split()[0],
'deployment_id': settings.DEPLOYMENT_ID,
}
try:
requests.get(
url=settings.CENSUS_URL,
params=census_data,
timeout=3,
proxies=settings.HTTP_PROXIES
)
except requests.exceptions.RequestException:
pass

View File

@@ -570,8 +570,9 @@ class SystemView(UserPassesTestMixin, View):
return response return response
# Serialize any CustomValidator classes # Serialize any CustomValidator classes
if hasattr(config, 'CUSTOM_VALIDATORS') and config.CUSTOM_VALIDATORS: for attr in ['CUSTOM_VALIDATORS', 'PROTECTION_RULES']:
config.CUSTOM_VALIDATORS = json.dumps(config.CUSTOM_VALIDATORS, cls=ConfigJSONEncoder, indent=4) if hasattr(config, attr) and getattr(config, attr, None):
setattr(config, attr, json.dumps(getattr(config, attr), cls=ConfigJSONEncoder, indent=4))
return render(request, 'core/system.html', { return render(request, 'core/system.html', {
'stats': stats, 'stats': stats,
@@ -594,7 +595,7 @@ class BasePluginView(UserPassesTestMixin, View):
catalog_plugins_error = cache.get(self.CACHE_KEY_CATALOG_ERROR, default=False) catalog_plugins_error = cache.get(self.CACHE_KEY_CATALOG_ERROR, default=False)
if not catalog_plugins_error: if not catalog_plugins_error:
catalog_plugins = get_catalog_plugins() catalog_plugins = get_catalog_plugins()
if not catalog_plugins: if not catalog_plugins and not settings.ISOLATED_DEPLOYMENT:
# Cache for 5 minutes to avoid spamming connection # Cache for 5 minutes to avoid spamming connection
cache.set(self.CACHE_KEY_CATALOG_ERROR, True, 300) cache.set(self.CACHE_KEY_CATALOG_ERROR, True, 300)
messages.warning(request, _("Plugins catalog could not be loaded")) messages.warning(request, _("Plugins catalog could not be loaded"))

View File

@@ -170,8 +170,8 @@ class MACAddressSerializer(NetBoxModelSerializer):
class Meta: class Meta:
model = MACAddress model = MACAddress
fields = [ fields = [
'id', 'url', 'display_url', 'display', 'mac_address', 'assigned_object_type', 'assigned_object', 'id', 'url', 'display_url', 'display', 'mac_address', 'assigned_object_type', 'assigned_object_id',
'description', 'comments', 'assigned_object', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
] ]
brief_fields = ('id', 'url', 'display', 'mac_address', 'description') brief_fields = ('id', 'url', 'display', 'mac_address', 'description')

View File

@@ -374,21 +374,26 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
if not self._state.adding: if not self._state.adding:
mounted_devices = Device.objects.filter(rack=self).exclude(position__isnull=True).order_by('position') mounted_devices = Device.objects.filter(rack=self).exclude(position__isnull=True).order_by('position')
effective_u_height = self.rack_type.u_height if self.rack_type else self.u_height
effective_starting_unit = self.rack_type.starting_unit if self.rack_type else self.starting_unit
# Validate that Rack is tall enough to house the highest mounted Device # Validate that Rack is tall enough to house the highest mounted Device
if top_device := mounted_devices.last(): if top_device := mounted_devices.last():
min_height = top_device.position + top_device.device_type.u_height - self.starting_unit min_height = top_device.position + top_device.device_type.u_height - effective_starting_unit
if self.u_height < min_height: if effective_u_height < min_height:
field = 'rack_type' if self.rack_type else 'u_height'
raise ValidationError({ raise ValidationError({
'u_height': _( field: _(
"Rack must be at least {min_height}U tall to house currently installed devices." "Rack must be at least {min_height}U tall to house currently installed devices."
).format(min_height=min_height) ).format(min_height=min_height)
}) })
# Validate that the Rack's starting unit is less than or equal to the position of the lowest mounted Device # Validate that the Rack's starting unit is less than or equal to the position of the lowest mounted Device
if last_device := mounted_devices.first(): if last_device := mounted_devices.first():
if self.starting_unit > last_device.position: if effective_starting_unit > last_device.position:
field = 'rack_type' if self.rack_type else 'starting_unit'
raise ValidationError({ raise ValidationError({
'starting_unit': _("Rack unit numbering must begin at {position} or less to house " field: _("Rack unit numbering must begin at {position} or less to house "
"currently installed devices.").format(position=last_device.position) "currently installed devices.").format(position=last_device.position)
}) })

View File

@@ -69,7 +69,11 @@ INTERFACE_FHRPGROUPS = """
""" """
INTERFACE_TAGGED_VLANS = """ INTERFACE_TAGGED_VLANS = """
{% if record.mode == 'tagged' %} {% load i18n %}
{% if record.mode == 'access' %}
{% elif record.mode == 'tagged-all' %}
{% trans "All" %}
{% else %}
{% if value.count > 3 %} {% if value.count > 3 %}
<a href="{% url 'ipam:vlan_list' %}?{{ record|meta:"model_name" }}_id={{ record.pk }}">{{ value.count }} VLANs</a> <a href="{% url 'ipam:vlan_list' %}?{{ record|meta:"model_name" }}_id={{ record.pk }}">{{ value.count }} VLANs</a>
{% else %} {% else %}
@@ -77,8 +81,6 @@ INTERFACE_TAGGED_VLANS = """
<a href="{{ vlan.get_absolute_url }}">{{ vlan }}</a><br /> <a href="{{ vlan.get_absolute_url }}">{{ vlan }}</a><br />
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% elif record.mode == 'tagged-all' %}
All
{% endif %} {% endif %}
""" """

View File

@@ -2447,3 +2447,46 @@ class VirtualDeviceContextTest(APIViewTestCases.APIViewTestCase):
# Omit identifier to test uniqueness constraint # Omit identifier to test uniqueness constraint
}, },
] ]
class MACAddressTest(APIViewTestCases.APIViewTestCase):
model = MACAddress
brief_fields = ['description', 'display', 'id', 'mac_address', 'url']
bulk_update_data = {
'description': 'New description',
}
@classmethod
def setUpTestData(cls):
device = create_test_device(name='Device 1')
interfaces = (
Interface(device=device, name='Interface 1', type='1000base-t'),
Interface(device=device, name='Interface 2', type='1000base-t'),
Interface(device=device, name='Interface 3', type='1000base-t'),
Interface(device=device, name='Interface 4', type='1000base-t'),
Interface(device=device, name='Interface 5', type='1000base-t'),
)
Interface.objects.bulk_create(interfaces)
mac_addresses = (
MACAddress(mac_address='00:00:00:00:00:01', assigned_object=interfaces[0]),
MACAddress(mac_address='00:00:00:00:00:02', assigned_object=interfaces[1]),
MACAddress(mac_address='00:00:00:00:00:03', assigned_object=interfaces[2]),
)
MACAddress.objects.bulk_create(mac_addresses)
cls.create_data = [
{
'mac_address': '00:00:00:00:00:04',
'assigned_object_type': 'dcim.interface',
'assigned_object_id': interfaces[3].pk,
},
{
'mac_address': '00:00:00:00:00:05',
'assigned_object_type': 'dcim.interface',
'assigned_object_id': interfaces[4].pk,
},
{
'mac_address': '00:00:00:00:00:06',
},
]

View File

@@ -3470,3 +3470,54 @@ class VirtualDeviceContextTestCase(ViewTestCases.PrimaryObjectViewTestCase):
cls.bulk_edit_data = { cls.bulk_edit_data = {
'status': VirtualDeviceContextStatusChoices.STATUS_OFFLINE, 'status': VirtualDeviceContextStatusChoices.STATUS_OFFLINE,
} }
class MACAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
model = MACAddress
@classmethod
def setUpTestData(cls):
device = create_test_device(name='Device 1')
interfaces = (
Interface(device=device, name='Interface 1', type='1000base-t'),
Interface(device=device, name='Interface 2', type='1000base-t'),
Interface(device=device, name='Interface 3', type='1000base-t'),
Interface(device=device, name='Interface 4', type='1000base-t'),
Interface(device=device, name='Interface 5', type='1000base-t'),
Interface(device=device, name='Interface 6', type='1000base-t'),
)
Interface.objects.bulk_create(interfaces)
mac_addresses = (
MACAddress(mac_address='00:00:00:00:00:01', assigned_object=interfaces[0]),
MACAddress(mac_address='00:00:00:00:00:02', assigned_object=interfaces[1]),
MACAddress(mac_address='00:00:00:00:00:03', assigned_object=interfaces[2]),
)
MACAddress.objects.bulk_create(mac_addresses)
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'mac_address': EUI('00:00:00:00:00:04'),
'description': 'New MAC address',
'interface_id': interfaces[3].pk,
'tags': [t.pk for t in tags],
}
cls.csv_data = (
"mac_address,device,interface",
"00:00:00:00:00:04,Device 1,Interface 4",
"00:00:00:00:00:05,Device 1,Interface 5",
"00:00:00:00:00:06,Device 1,Interface 6",
)
cls.csv_update_data = (
"id,mac_address",
f"{mac_addresses[0].pk},00:00:00:00:00:0a",
f"{mac_addresses[1].pk},00:00:00:00:00:0b",
f"{mac_addresses[2].pk},00:00:00:00:00:0c",
)
cls.bulk_edit_data = {
'description': 'New description',
}

View File

@@ -15,7 +15,7 @@ from jinja2.exceptions import TemplateError
from circuits.models import Circuit, CircuitTermination from circuits.models import Circuit, CircuitTermination
from extras.views import ObjectConfigContextView from extras.views import ObjectConfigContextView
from ipam.models import ASN, IPAddress, VLANGroup from ipam.models import ASN, IPAddress, Prefix, VLANGroup
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
from netbox.constants import DEFAULT_ACTION_PERMISSIONS from netbox.constants import DEFAULT_ACTION_PERMISSIONS
from netbox.views import generic from netbox.views import generic
@@ -30,8 +30,9 @@ from utilities.views import (
) )
from virtualization.filtersets import VirtualMachineFilterSet from virtualization.filtersets import VirtualMachineFilterSet
from virtualization.forms import VirtualMachineFilterForm from virtualization.forms import VirtualMachineFilterForm
from virtualization.models import VirtualMachine from virtualization.models import Cluster, VirtualMachine
from virtualization.tables import VirtualMachineTable from virtualization.tables import VirtualMachineTable
from wireless.models import WirelessLAN
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .choices import DeviceFaceChoices, InterfaceModeChoices from .choices import DeviceFaceChoices, InterfaceModeChoices
from .models import * from .models import *
@@ -238,6 +239,7 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView):
'related_models': self.get_related_models( 'related_models': self.get_related_models(
request, request,
regions, regions,
omit=(Cluster, Prefix, WirelessLAN),
extra=( extra=(
(Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'), (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
(Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'), (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
@@ -247,6 +249,11 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView):
).distinct(), ).distinct(),
'region_id' 'region_id'
), ),
# Handle these relations manually to avoid erroneous filter name resolution
(Cluster.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
(Prefix.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
(WirelessLAN.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
), ),
), ),
} }
@@ -331,6 +338,7 @@ class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
'related_models': self.get_related_models( 'related_models': self.get_related_models(
request, request,
groups, groups,
omit=(Cluster, Prefix, WirelessLAN),
extra=( extra=(
(Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
(Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'), (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
@@ -340,6 +348,20 @@ class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
).distinct(), ).distinct(),
'site_group_id' 'site_group_id'
), ),
# Handle these relations manually to avoid erroneous filter name resolution
(
Cluster.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
'site_group_id'
),
(
Prefix.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
'site_group_id'
),
(
WirelessLAN.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
'site_group_id'
),
), ),
), ),
} }
@@ -418,8 +440,8 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
'related_models': self.get_related_models( 'related_models': self.get_related_models(
request, request,
instance, instance,
[CableTermination, CircuitTermination], omit=(CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN),
( extra=(
(VLANGroup.objects.restrict(request.user, 'view').filter( (VLANGroup.objects.restrict(request.user, 'view').filter(
scope_type=ContentType.objects.get_for_model(Site), scope_type=ContentType.objects.get_for_model(Site),
scope_id=instance.pk scope_id=instance.pk
@@ -429,6 +451,11 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(), Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(),
'site_id' 'site_id'
), ),
# Handle these relations manually to avoid erroneous filter name resolution
(Cluster.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
(Prefix.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
(WirelessLAN.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
), ),
), ),
} }
@@ -506,14 +533,19 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView):
'related_models': self.get_related_models( 'related_models': self.get_related_models(
request, request,
locations, locations,
[CableTermination], omit=[CableTermination, Cluster, Prefix, WirelessLAN],
( extra=(
( (
Circuit.objects.restrict(request.user, 'view').filter( Circuit.objects.restrict(request.user, 'view').filter(
terminations___location=instance terminations___location=instance
).distinct(), ).distinct(),
'location_id' 'location_id'
), ),
# Handle these relations manually to avoid erroneous filter name resolution
(Cluster.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
(Prefix.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
(WirelessLAN.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
), ),
), ),
} }

View File

@@ -314,7 +314,7 @@ class RSSFeedWidget(DashboardWidget):
return f'dashboard_rss_{url_checksum}' return f'dashboard_rss_{url_checksum}'
def get_feed(self): def get_feed(self):
if self.config['requires_internet'] and settings.ISOLATED_DEPLOYMENT: if self.config.get('requires_internet') and settings.ISOLATED_DEPLOYMENT:
return { return {
'isolated_deployment': True, 'isolated_deployment': True,
} }

View File

@@ -120,11 +120,12 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
is_active=True, is_active=True,
) )
# Apply Location & DeviceType filters only for VirtualMachines
if self.model._meta.model_name == 'device': if self.model._meta.model_name == 'device':
base_query.add((Q(locations=OuterRef('location')) | Q(locations=None)), Q.AND) base_query.add((Q(locations=OuterRef('location')) | Q(locations=None)), Q.AND)
base_query.add((Q(device_types=OuterRef('device_type')) | Q(device_types=None)), Q.AND) base_query.add((Q(device_types=OuterRef('device_type')) | Q(device_types=None)), Q.AND)
elif self.model._meta.model_name == 'virtualmachine': elif self.model._meta.model_name == 'virtualmachine':
base_query.add(Q(locations=None), Q.AND)
base_query.add(Q(device_types=None), Q.AND) base_query.add(Q(device_types=None), Q.AND)
base_query.add((Q(roles=OuterRef('role')) | Q(roles=None)), Q.AND) base_query.add((Q(roles=OuterRef('role')) | Q(roles=None)), Q.AND)

View File

@@ -79,7 +79,7 @@ class PrefixIndex(SearchIndex):
('description', 500), ('description', 500),
('comments', 5000), ('comments', 5000),
) )
display_attrs = ('site', 'vrf', 'tenant', 'vlan', 'status', 'role', 'description') display_attrs = ('scope', 'vrf', 'tenant', 'vlan', 'status', 'role', 'description')
@register_search @register_search

View File

@@ -192,7 +192,8 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
) )
scope = tables.Column( scope = tables.Column(
verbose_name=_('Scope'), verbose_name=_('Scope'),
linkify=True linkify=True,
orderable=False
) )
vlan_group = tables.Column( vlan_group = tables.Column(
accessor='vlan__group', accessor='vlan__group',
@@ -200,6 +201,7 @@ class PrefixTable(TenancyColumnsMixin, NetBoxTable):
verbose_name=_('VLAN Group') verbose_name=_('VLAN Group')
) )
vlan = tables.Column( vlan = tables.Column(
order_by=('vlan__vid', 'vlan__pk'),
linkify=True, linkify=True,
verbose_name=_('VLAN') verbose_name=_('VLAN')
) )

View File

@@ -5,9 +5,7 @@ import os
import platform import platform
import sys import sys
import warnings import warnings
from urllib.parse import urlencode
import requests
from django.contrib.messages import constants as messages from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.validators import URLValidator from django.core.validators import URLValidator
@@ -224,8 +222,18 @@ DATABASES = {
# Storage backend # Storage backend
# #
# Default STORAGES for Django
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}
if STORAGE_BACKEND is not None: if STORAGE_BACKEND is not None:
DEFAULT_FILE_STORAGE = STORAGE_BACKEND STORAGES['default']['BACKEND'] = STORAGE_BACKEND
# django-storages # django-storages
if STORAGE_BACKEND.startswith('storages.'): if STORAGE_BACKEND.startswith('storages.'):
@@ -583,17 +591,6 @@ if SENTRY_ENABLED:
# Calculate a unique deployment ID from the secret key # Calculate a unique deployment ID from the secret key
DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16] DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16]
CENSUS_URL = 'https://census.netbox.oss.netboxlabs.com/api/v1/' CENSUS_URL = 'https://census.netbox.oss.netboxlabs.com/api/v1/'
CENSUS_PARAMS = {
'version': RELEASE.full_version,
'python_version': sys.version.split()[0],
'deployment_id': DEPLOYMENT_ID,
}
if CENSUS_REPORTING_ENABLED and not ISOLATED_DEPLOYMENT and not DEBUG and 'test' not in sys.argv:
try:
# Report anonymous census data
requests.get(f'{CENSUS_URL}?{urlencode(CENSUS_PARAMS)}', timeout=3, proxies=HTTP_PROXIES)
except requests.exceptions.RequestException:
pass
# #

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -27,10 +27,10 @@
"bootstrap": "5.3.3", "bootstrap": "5.3.3",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"flatpickr": "4.6.13", "flatpickr": "4.6.13",
"gridstack": "11.2.0", "gridstack": "11.3.0",
"htmx.org": "1.9.12", "htmx.org": "1.9.12",
"query-string": "9.1.1", "query-string": "9.1.1",
"sass": "1.83.1", "sass": "1.83.4",
"tom-select": "2.4.1", "tom-select": "2.4.1",
"typeface-inter": "3.18.1", "typeface-inter": "3.18.1",
"typeface-roboto-mono": "1.1.13" "typeface-roboto-mono": "1.1.13"

View File

@@ -1905,10 +1905,10 @@ graphql@16.10.0:
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.10.0.tgz#24c01ae0af6b11ea87bf55694429198aaa8e220c" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.10.0.tgz#24c01ae0af6b11ea87bf55694429198aaa8e220c"
integrity sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ== integrity sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==
gridstack@11.2.0: gridstack@11.3.0:
version "11.2.0" version "11.3.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-11.2.0.tgz#8977a6632c521260f064ef171b92c7a8df4f58a9" resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-11.3.0.tgz#b110c66bafc64c920fc54933e2c9df4f7b2cfffe"
integrity sha512-ajwUzd9spR8NXDxfJotHWq9WOYoDOV9o6UJR3ksevNz8cvXNxDtI9H/lC+RN6ijM2DexureLlsG0RpYjBZiOtg== integrity sha512-Z0eRovKcZTRTs3zetJwjO6CNwrgIy845WfOeZGk8ybpeMCE8fMA8tScyKU72Y2M6uGHkjgwnjflglvPiv+RcBQ==
has-bigints@^1.0.1, has-bigints@^1.0.2: has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2" version "1.0.2"
@@ -2667,10 +2667,10 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0" es-errors "^1.3.0"
is-regex "^1.1.4" is-regex "^1.1.4"
sass@1.83.1: sass@1.83.4:
version "1.83.1" version "1.83.4"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.1.tgz#dee1ab94b47a6f9993d3195d36f556bcbda64846" resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.4.tgz#5ccf60f43eb61eeec300b780b8dcb85f16eec6d1"
integrity sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA== integrity sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==
dependencies: dependencies:
chokidar "^4.0.0" chokidar "^4.0.0"
immutable "^5.0.2" immutable "^5.0.2"

View File

@@ -1,3 +1,3 @@
version: "4.2.0" version: "4.2.2"
edition: "Community" edition: "Community"
published: "2025-01-06" published: "2025-01-17"

View File

@@ -103,7 +103,7 @@
<tr> <tr>
<th scope="row" class="border-0 ps-3">{% trans "Protection rules" %}</th> <th scope="row" class="border-0 ps-3">{% trans "Protection rules" %}</th>
{% if config.PROTECTION_RULES %} {% if config.PROTECTION_RULES %}
<td class="border-0"><pre>{{ config.PROTECTION_RULES|json }}</pre></td> <td class="border-0"><pre>{{ config.PROTECTION_RULES }}</pre></td>
{% else %} {% else %}
<td class="border-0">{{ ''|placeholder }}</td> <td class="border-0">{{ ''|placeholder }}</td>
{% endif %} {% endif %}

View File

@@ -1,2 +0,0 @@
<li class="breadcrumb-item"><a href="{% url 'dcim:devicetype_list' %}?manufacturer_id={{ object.manufacturer.pk }}">{{ object.manufacturer }}</a></li>

View File

@@ -8,7 +8,9 @@
{% block breadcrumbs %} {% block breadcrumbs %}
{{ block.super }} {{ block.super }}
{% include 'dcim/inc/devicetype_breadcrumbs.html' %} <li class="breadcrumb-item">
<a href="{% url 'dcim:moduletype_list' %}?manufacturer_id={{ object.manufacturer.pk }}">{{ object.manufacturer }}</a>
</li>
{% endblock %} {% endblock %}
{% block extra_controls %} {% block extra_controls %}

View File

@@ -3,13 +3,6 @@
{% load helpers %} {% load helpers %}
{% load i18n %} {% load i18n %}
{% block title %}{{ object.manufacturer }} {{ object.model }}{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
{% include 'dcim/inc/devicetype_breadcrumbs.html' %}
{% endblock %}
{% block extra_controls %} {% block extra_controls %}
{% include 'dcim/inc/moduletype_buttons.html' %} {% include 'dcim/inc/moduletype_buttons.html' %}
{% endblock %} {% endblock %}

View File

@@ -5,7 +5,7 @@
<div class="list-group-item px-1 py-2"> <div class="list-group-item px-1 py-2">
<a href="{{ entry.link }}" class="text-body">{{ entry.title }}</a> <a href="{{ entry.link }}" class="text-body">{{ entry.title }}</a>
<div class="text-secondary"> <div class="text-secondary">
{{ entry.summary|safe }} {{ entry.summary }}
</div> </div>
</div> </div>
{% empty %} {% empty %}

View File

@@ -7,15 +7,16 @@
# czarnian, 2024 # czarnian, 2024
# Jeremy Stretch, 2024 # Jeremy Stretch, 2024
# Pavel Valach, 2024 # Pavel Valach, 2024
# Matěj Gordon, 2025
# #
#, fuzzy #, fuzzy
msgid "" 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: 2024-12-12 05:02+0000\n" "POT-Creation-Date: 2025-01-04 05:02+0000\n"
"PO-Revision-Date: 2023-10-30 17:48+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n"
"Last-Translator: Pavel Valach, 2024\n" "Last-Translator: Matěj Gordon, 2025\n"
"Language-Team: Czech (https://app.transifex.com/netbox-community/teams/178115/cs/)\n" "Language-Team: Czech (https://app.transifex.com/netbox-community/teams/178115/cs/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -151,7 +152,7 @@ msgstr "Neaktivní"
#: netbox/dcim/filtersets.py:464 netbox/dcim/filtersets.py:1021 #: netbox/dcim/filtersets.py:464 netbox/dcim/filtersets.py:1021
#: netbox/dcim/filtersets.py:1368 netbox/dcim/filtersets.py:1903 #: netbox/dcim/filtersets.py:1368 netbox/dcim/filtersets.py:1903
#: netbox/dcim/filtersets.py:2146 netbox/dcim/filtersets.py:2204 #: netbox/dcim/filtersets.py:2146 netbox/dcim/filtersets.py:2204
#: netbox/ipam/filtersets.py:339 netbox/ipam/filtersets.py:959 #: netbox/ipam/filtersets.py:341 netbox/ipam/filtersets.py:961
#: netbox/virtualization/filtersets.py:45 #: netbox/virtualization/filtersets.py:45
#: netbox/virtualization/filtersets.py:173 netbox/vpn/filtersets.py:358 #: netbox/virtualization/filtersets.py:173 netbox/vpn/filtersets.py:358
msgid "Region (ID)" msgid "Region (ID)"
@@ -163,8 +164,8 @@ msgstr "Region (ID)"
#: netbox/dcim/filtersets.py:471 netbox/dcim/filtersets.py:1028 #: netbox/dcim/filtersets.py:471 netbox/dcim/filtersets.py:1028
#: netbox/dcim/filtersets.py:1375 netbox/dcim/filtersets.py:1910 #: netbox/dcim/filtersets.py:1375 netbox/dcim/filtersets.py:1910
#: netbox/dcim/filtersets.py:2153 netbox/dcim/filtersets.py:2211 #: netbox/dcim/filtersets.py:2153 netbox/dcim/filtersets.py:2211
#: netbox/extras/filtersets.py:509 netbox/ipam/filtersets.py:346 #: netbox/extras/filtersets.py:509 netbox/ipam/filtersets.py:348
#: netbox/ipam/filtersets.py:966 netbox/virtualization/filtersets.py:52 #: netbox/ipam/filtersets.py:968 netbox/virtualization/filtersets.py:52
#: netbox/virtualization/filtersets.py:180 netbox/vpn/filtersets.py:353 #: netbox/virtualization/filtersets.py:180 netbox/vpn/filtersets.py:353
msgid "Region (slug)" msgid "Region (slug)"
msgstr "Region (zkratka)" msgstr "Region (zkratka)"
@@ -174,8 +175,8 @@ msgstr "Region (zkratka)"
#: netbox/dcim/filtersets.py:346 netbox/dcim/filtersets.py:477 #: netbox/dcim/filtersets.py:346 netbox/dcim/filtersets.py:477
#: netbox/dcim/filtersets.py:1034 netbox/dcim/filtersets.py:1381 #: netbox/dcim/filtersets.py:1034 netbox/dcim/filtersets.py:1381
#: netbox/dcim/filtersets.py:1916 netbox/dcim/filtersets.py:2159 #: netbox/dcim/filtersets.py:1916 netbox/dcim/filtersets.py:2159
#: netbox/dcim/filtersets.py:2217 netbox/ipam/filtersets.py:352 #: netbox/dcim/filtersets.py:2217 netbox/ipam/filtersets.py:354
#: netbox/ipam/filtersets.py:972 netbox/virtualization/filtersets.py:58 #: netbox/ipam/filtersets.py:974 netbox/virtualization/filtersets.py:58
#: netbox/virtualization/filtersets.py:186 #: netbox/virtualization/filtersets.py:186
msgid "Site group (ID)" msgid "Site group (ID)"
msgstr "Skupina stránek (ID)" msgstr "Skupina stránek (ID)"
@@ -186,7 +187,7 @@ msgstr "Skupina stránek (ID)"
#: netbox/dcim/filtersets.py:1041 netbox/dcim/filtersets.py:1388 #: netbox/dcim/filtersets.py:1041 netbox/dcim/filtersets.py:1388
#: netbox/dcim/filtersets.py:1923 netbox/dcim/filtersets.py:2166 #: netbox/dcim/filtersets.py:1923 netbox/dcim/filtersets.py:2166
#: netbox/dcim/filtersets.py:2224 netbox/extras/filtersets.py:515 #: netbox/dcim/filtersets.py:2224 netbox/extras/filtersets.py:515
#: netbox/ipam/filtersets.py:359 netbox/ipam/filtersets.py:979 #: netbox/ipam/filtersets.py:361 netbox/ipam/filtersets.py:981
#: netbox/virtualization/filtersets.py:65 #: netbox/virtualization/filtersets.py:65
#: netbox/virtualization/filtersets.py:193 #: netbox/virtualization/filtersets.py:193
msgid "Site group (slug)" msgid "Site group (slug)"
@@ -256,8 +257,8 @@ msgstr "Stránky"
#: netbox/circuits/filtersets.py:62 netbox/circuits/filtersets.py:229 #: netbox/circuits/filtersets.py:62 netbox/circuits/filtersets.py:229
#: netbox/circuits/filtersets.py:274 netbox/dcim/filtersets.py:242 #: netbox/circuits/filtersets.py:274 netbox/dcim/filtersets.py:242
#: netbox/dcim/filtersets.py:363 netbox/dcim/filtersets.py:458 #: netbox/dcim/filtersets.py:363 netbox/dcim/filtersets.py:458
#: netbox/extras/filtersets.py:531 netbox/ipam/filtersets.py:238 #: netbox/extras/filtersets.py:531 netbox/ipam/filtersets.py:240
#: netbox/ipam/filtersets.py:369 netbox/ipam/filtersets.py:989 #: netbox/ipam/filtersets.py:371 netbox/ipam/filtersets.py:991
#: netbox/virtualization/filtersets.py:75 #: netbox/virtualization/filtersets.py:75
#: netbox/virtualization/filtersets.py:203 netbox/vpn/filtersets.py:363 #: netbox/virtualization/filtersets.py:203 netbox/vpn/filtersets.py:363
msgid "Site (slug)" msgid "Site (slug)"
@@ -276,13 +277,13 @@ msgstr "ASN"
#: netbox/circuits/filtersets.py:95 netbox/circuits/filtersets.py:122 #: netbox/circuits/filtersets.py:95 netbox/circuits/filtersets.py:122
#: netbox/circuits/filtersets.py:156 netbox/circuits/filtersets.py:283 #: netbox/circuits/filtersets.py:156 netbox/circuits/filtersets.py:283
#: netbox/circuits/filtersets.py:325 netbox/ipam/filtersets.py:243 #: netbox/circuits/filtersets.py:325 netbox/ipam/filtersets.py:245
msgid "Provider (ID)" msgid "Provider (ID)"
msgstr "Poskytovatel (ID)" msgstr "Poskytovatel (ID)"
#: netbox/circuits/filtersets.py:101 netbox/circuits/filtersets.py:128 #: netbox/circuits/filtersets.py:101 netbox/circuits/filtersets.py:128
#: netbox/circuits/filtersets.py:162 netbox/circuits/filtersets.py:289 #: netbox/circuits/filtersets.py:162 netbox/circuits/filtersets.py:289
#: netbox/circuits/filtersets.py:331 netbox/ipam/filtersets.py:249 #: netbox/circuits/filtersets.py:331 netbox/ipam/filtersets.py:251
msgid "Provider (slug)" msgid "Provider (slug)"
msgstr "Poskytovatel (slug)" msgstr "Poskytovatel (slug)"
@@ -311,8 +312,8 @@ msgstr "Typ okruhu (URL zkratka)"
#: netbox/dcim/filtersets.py:452 netbox/dcim/filtersets.py:1045 #: netbox/dcim/filtersets.py:452 netbox/dcim/filtersets.py:1045
#: netbox/dcim/filtersets.py:1393 netbox/dcim/filtersets.py:1928 #: netbox/dcim/filtersets.py:1393 netbox/dcim/filtersets.py:1928
#: netbox/dcim/filtersets.py:2170 netbox/dcim/filtersets.py:2229 #: netbox/dcim/filtersets.py:2170 netbox/dcim/filtersets.py:2229
#: netbox/ipam/filtersets.py:232 netbox/ipam/filtersets.py:363 #: netbox/ipam/filtersets.py:234 netbox/ipam/filtersets.py:365
#: netbox/ipam/filtersets.py:983 netbox/virtualization/filtersets.py:69 #: netbox/ipam/filtersets.py:985 netbox/virtualization/filtersets.py:69
#: netbox/virtualization/filtersets.py:197 netbox/vpn/filtersets.py:368 #: netbox/virtualization/filtersets.py:197 netbox/vpn/filtersets.py:368
msgid "Site (ID)" msgid "Site (ID)"
msgstr "Stránky (ID)" msgstr "Stránky (ID)"
@@ -666,7 +667,7 @@ msgstr "Účet poskytovatele"
#: netbox/dcim/forms/filtersets.py:924 netbox/dcim/forms/filtersets.py:958 #: netbox/dcim/forms/filtersets.py:924 netbox/dcim/forms/filtersets.py:958
#: netbox/dcim/forms/filtersets.py:1059 netbox/dcim/forms/filtersets.py:1170 #: netbox/dcim/forms/filtersets.py:1059 netbox/dcim/forms/filtersets.py:1170
#: netbox/dcim/tables/devices.py:140 netbox/dcim/tables/devices.py:817 #: netbox/dcim/tables/devices.py:140 netbox/dcim/tables/devices.py:817
#: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:69 #: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:70
#: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:126 #: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:126
#: netbox/dcim/tables/sites.py:82 netbox/dcim/tables/sites.py:138 #: netbox/dcim/tables/sites.py:82 netbox/dcim/tables/sites.py:138
#: netbox/ipam/forms/bulk_edit.py:256 netbox/ipam/forms/bulk_edit.py:306 #: netbox/ipam/forms/bulk_edit.py:256 netbox/ipam/forms/bulk_edit.py:306
@@ -1101,7 +1102,7 @@ msgstr "Přiřazení"
#: netbox/circuits/tables/circuits.py:155 netbox/dcim/forms/bulk_edit.py:118 #: netbox/circuits/tables/circuits.py:155 netbox/dcim/forms/bulk_edit.py:118
#: netbox/dcim/forms/bulk_import.py:100 netbox/dcim/forms/model_forms.py:117 #: netbox/dcim/forms/bulk_import.py:100 netbox/dcim/forms/model_forms.py:117
#: netbox/dcim/tables/sites.py:89 netbox/extras/forms/filtersets.py:480 #: netbox/dcim/tables/sites.py:89 netbox/extras/forms/filtersets.py:480
#: netbox/ipam/filtersets.py:999 netbox/ipam/forms/bulk_edit.py:493 #: netbox/ipam/filtersets.py:1001 netbox/ipam/forms/bulk_edit.py:493
#: netbox/ipam/forms/bulk_import.py:460 netbox/ipam/forms/model_forms.py:561 #: netbox/ipam/forms/bulk_import.py:460 netbox/ipam/forms/model_forms.py:561
#: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:122 #: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:122
#: netbox/ipam/tables/vlans.py:226 #: netbox/ipam/tables/vlans.py:226
@@ -1540,7 +1541,7 @@ msgstr "Míra odevzdání"
#: netbox/circuits/tables/providers.py:82 #: netbox/circuits/tables/providers.py:82
#: netbox/circuits/tables/providers.py:107 netbox/dcim/tables/devices.py:1036 #: netbox/circuits/tables/providers.py:107 netbox/dcim/tables/devices.py:1036
#: netbox/dcim/tables/devicetypes.py:92 netbox/dcim/tables/modules.py:29 #: netbox/dcim/tables/devicetypes.py:92 netbox/dcim/tables/modules.py:29
#: netbox/dcim/tables/modules.py:72 netbox/dcim/tables/power.py:39 #: netbox/dcim/tables/modules.py:73 netbox/dcim/tables/power.py:39
#: netbox/dcim/tables/power.py:96 netbox/dcim/tables/racks.py:84 #: netbox/dcim/tables/power.py:96 netbox/dcim/tables/racks.py:84
#: netbox/dcim/tables/racks.py:145 netbox/dcim/tables/racks.py:225 #: netbox/dcim/tables/racks.py:145 netbox/dcim/tables/racks.py:225
#: netbox/dcim/tables/sites.py:108 netbox/extras/tables/tables.py:582 #: netbox/dcim/tables/sites.py:108 netbox/extras/tables/tables.py:582
@@ -2932,7 +2933,7 @@ msgid "Parent site group (slug)"
msgstr "Nadřazená skupina stránek (slimák)" msgstr "Nadřazená skupina stránek (slimák)"
#: netbox/dcim/filtersets.py:164 netbox/extras/filtersets.py:364 #: netbox/dcim/filtersets.py:164 netbox/extras/filtersets.py:364
#: netbox/ipam/filtersets.py:841 netbox/ipam/filtersets.py:993 #: netbox/ipam/filtersets.py:843 netbox/ipam/filtersets.py:995
msgid "Group (ID)" msgid "Group (ID)"
msgstr "Skupina (ID)" msgstr "Skupina (ID)"
@@ -2990,15 +2991,15 @@ msgstr "Typ stojanu (ID)"
#: netbox/dcim/filtersets.py:411 netbox/dcim/filtersets.py:892 #: netbox/dcim/filtersets.py:411 netbox/dcim/filtersets.py:892
#: netbox/dcim/filtersets.py:994 netbox/dcim/filtersets.py:1850 #: netbox/dcim/filtersets.py:994 netbox/dcim/filtersets.py:1850
#: netbox/ipam/filtersets.py:381 netbox/ipam/filtersets.py:493 #: netbox/ipam/filtersets.py:383 netbox/ipam/filtersets.py:495
#: netbox/ipam/filtersets.py:1003 netbox/virtualization/filtersets.py:210 #: netbox/ipam/filtersets.py:1005 netbox/virtualization/filtersets.py:210
msgid "Role (ID)" msgid "Role (ID)"
msgstr "Role (ID)" msgstr "Role (ID)"
#: netbox/dcim/filtersets.py:417 netbox/dcim/filtersets.py:898 #: netbox/dcim/filtersets.py:417 netbox/dcim/filtersets.py:898
#: netbox/dcim/filtersets.py:1000 netbox/dcim/filtersets.py:1856 #: netbox/dcim/filtersets.py:1000 netbox/dcim/filtersets.py:1856
#: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:387 #: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:389
#: netbox/ipam/filtersets.py:499 netbox/ipam/filtersets.py:1009 #: netbox/ipam/filtersets.py:501 netbox/ipam/filtersets.py:1011
#: netbox/virtualization/filtersets.py:216 #: netbox/virtualization/filtersets.py:216
msgid "Role (slug)" msgid "Role (slug)"
msgstr "Role (slug)" msgstr "Role (slug)"
@@ -3196,7 +3197,7 @@ msgstr "VDC (ID)"
msgid "Device model" msgid "Device model"
msgstr "Model zařízení" msgstr "Model zařízení"
#: netbox/dcim/filtersets.py:1267 netbox/ipam/filtersets.py:632 #: netbox/dcim/filtersets.py:1267 netbox/ipam/filtersets.py:634
#: netbox/vpn/filtersets.py:102 netbox/vpn/filtersets.py:401 #: netbox/vpn/filtersets.py:102 netbox/vpn/filtersets.py:401
msgid "Interface (ID)" msgid "Interface (ID)"
msgstr "Rozhraní (ID)" msgstr "Rozhraní (ID)"
@@ -3210,8 +3211,8 @@ msgid "Module bay (ID)"
msgstr "Modulová přihrádka (ID)" msgstr "Modulová přihrádka (ID)"
#: netbox/dcim/filtersets.py:1333 netbox/dcim/filtersets.py:1425 #: netbox/dcim/filtersets.py:1333 netbox/dcim/filtersets.py:1425
#: netbox/ipam/filtersets.py:611 netbox/ipam/filtersets.py:851 #: netbox/ipam/filtersets.py:613 netbox/ipam/filtersets.py:853
#: netbox/ipam/filtersets.py:1115 netbox/virtualization/filtersets.py:161 #: netbox/ipam/filtersets.py:1117 netbox/virtualization/filtersets.py:161
#: netbox/vpn/filtersets.py:379 #: netbox/vpn/filtersets.py:379
msgid "Device (ID)" msgid "Device (ID)"
msgstr "Zařízení (ID)" msgstr "Zařízení (ID)"
@@ -3220,8 +3221,8 @@ msgstr "Zařízení (ID)"
msgid "Rack (name)" msgid "Rack (name)"
msgstr "Stojan (název)" msgstr "Stojan (název)"
#: netbox/dcim/filtersets.py:1431 netbox/ipam/filtersets.py:606 #: netbox/dcim/filtersets.py:1431 netbox/ipam/filtersets.py:608
#: netbox/ipam/filtersets.py:846 netbox/ipam/filtersets.py:1121 #: netbox/ipam/filtersets.py:848 netbox/ipam/filtersets.py:1123
#: netbox/vpn/filtersets.py:374 #: netbox/vpn/filtersets.py:374
msgid "Device (name)" msgid "Device (name)"
msgstr "Zařízení (název)" msgstr "Zařízení (název)"
@@ -3273,9 +3274,9 @@ msgstr "Přiřazené VID"
#: netbox/dcim/forms/bulk_import.py:913 netbox/dcim/forms/filtersets.py:1428 #: netbox/dcim/forms/bulk_import.py:913 netbox/dcim/forms/filtersets.py:1428
#: netbox/dcim/forms/model_forms.py:1385 #: netbox/dcim/forms/model_forms.py:1385
#: netbox/dcim/models/device_components.py:711 #: netbox/dcim/models/device_components.py:711
#: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:316 #: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:318
#: netbox/ipam/filtersets.py:327 netbox/ipam/filtersets.py:483 #: netbox/ipam/filtersets.py:329 netbox/ipam/filtersets.py:485
#: netbox/ipam/filtersets.py:584 netbox/ipam/filtersets.py:595 #: netbox/ipam/filtersets.py:586 netbox/ipam/filtersets.py:597
#: netbox/ipam/forms/bulk_edit.py:242 netbox/ipam/forms/bulk_edit.py:298 #: netbox/ipam/forms/bulk_edit.py:242 netbox/ipam/forms/bulk_edit.py:298
#: netbox/ipam/forms/bulk_edit.py:340 netbox/ipam/forms/bulk_import.py:157 #: netbox/ipam/forms/bulk_edit.py:340 netbox/ipam/forms/bulk_import.py:157
#: netbox/ipam/forms/bulk_import.py:243 netbox/ipam/forms/bulk_import.py:279 #: netbox/ipam/forms/bulk_import.py:243 netbox/ipam/forms/bulk_import.py:279
@@ -3302,19 +3303,19 @@ msgstr "Přiřazené VID"
msgid "VRF" msgid "VRF"
msgstr "VRF" msgstr "VRF"
#: netbox/dcim/filtersets.py:1619 netbox/ipam/filtersets.py:322 #: netbox/dcim/filtersets.py:1619 netbox/ipam/filtersets.py:324
#: netbox/ipam/filtersets.py:333 netbox/ipam/filtersets.py:489 #: netbox/ipam/filtersets.py:335 netbox/ipam/filtersets.py:491
#: netbox/ipam/filtersets.py:590 netbox/ipam/filtersets.py:601 #: netbox/ipam/filtersets.py:592 netbox/ipam/filtersets.py:603
msgid "VRF (RD)" msgid "VRF (RD)"
msgstr "VRF (RD)" msgstr "VRF (RD)"
#: netbox/dcim/filtersets.py:1624 netbox/ipam/filtersets.py:1030 #: netbox/dcim/filtersets.py:1624 netbox/ipam/filtersets.py:1032
#: netbox/vpn/filtersets.py:342 #: netbox/vpn/filtersets.py:342
msgid "L2VPN (ID)" msgid "L2VPN (ID)"
msgstr "L2VPN (ID)" msgstr "L2VPN (ID)"
#: netbox/dcim/filtersets.py:1630 netbox/dcim/forms/filtersets.py:1433 #: netbox/dcim/filtersets.py:1630 netbox/dcim/forms/filtersets.py:1433
#: netbox/dcim/tables/devices.py:570 netbox/ipam/filtersets.py:1036 #: netbox/dcim/tables/devices.py:570 netbox/ipam/filtersets.py:1038
#: netbox/ipam/forms/filtersets.py:518 netbox/ipam/tables/vlans.py:137 #: netbox/ipam/forms/filtersets.py:518 netbox/ipam/tables/vlans.py:137
#: netbox/templates/dcim/interface.html:93 netbox/templates/ipam/vlan.html:66 #: netbox/templates/dcim/interface.html:93 netbox/templates/ipam/vlan.html:66
#: netbox/templates/vpn/l2vpntermination.html:12 #: netbox/templates/vpn/l2vpntermination.html:12
@@ -3476,7 +3477,7 @@ msgstr "Časové pásmo"
#: netbox/dcim/forms/object_import.py:187 netbox/dcim/tables/devices.py:96 #: netbox/dcim/forms/object_import.py:187 netbox/dcim/tables/devices.py:96
#: netbox/dcim/tables/devices.py:172 netbox/dcim/tables/devices.py:940 #: netbox/dcim/tables/devices.py:172 netbox/dcim/tables/devices.py:940
#: netbox/dcim/tables/devicetypes.py:80 netbox/dcim/tables/devicetypes.py:308 #: netbox/dcim/tables/devicetypes.py:80 netbox/dcim/tables/devicetypes.py:308
#: netbox/dcim/tables/modules.py:20 netbox/dcim/tables/modules.py:60 #: netbox/dcim/tables/modules.py:20 netbox/dcim/tables/modules.py:61
#: netbox/dcim/tables/racks.py:58 netbox/dcim/tables/racks.py:132 #: netbox/dcim/tables/racks.py:58 netbox/dcim/tables/racks.py:132
#: netbox/templates/dcim/devicetype.html:14 #: netbox/templates/dcim/devicetype.html:14
#: netbox/templates/dcim/inventoryitem.html:44 #: netbox/templates/dcim/inventoryitem.html:44
@@ -3727,7 +3728,7 @@ msgid "Device Type"
msgstr "Typ zařízení" msgstr "Typ zařízení"
#: netbox/dcim/forms/bulk_edit.py:598 netbox/dcim/forms/model_forms.py:401 #: netbox/dcim/forms/bulk_edit.py:598 netbox/dcim/forms/model_forms.py:401
#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:65 #: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66
#: netbox/templates/dcim/module.html:65 #: netbox/templates/dcim/module.html:65
#: netbox/templates/dcim/modulebay.html:66 #: netbox/templates/dcim/modulebay.html:66
#: netbox/templates/dcim/moduletype.html:22 #: netbox/templates/dcim/moduletype.html:22
@@ -3835,7 +3836,7 @@ msgstr "Klastr"
#: netbox/dcim/tables/devices.py:697 netbox/dcim/tables/devices.py:754 #: netbox/dcim/tables/devices.py:697 netbox/dcim/tables/devices.py:754
#: netbox/dcim/tables/devices.py:801 netbox/dcim/tables/devices.py:861 #: netbox/dcim/tables/devices.py:801 netbox/dcim/tables/devices.py:861
#: netbox/dcim/tables/devices.py:930 netbox/dcim/tables/devices.py:1057 #: netbox/dcim/tables/devices.py:930 netbox/dcim/tables/devices.py:1057
#: netbox/dcim/tables/modules.py:52 netbox/extras/forms/filtersets.py:321 #: netbox/dcim/tables/modules.py:53 netbox/extras/forms/filtersets.py:321
#: netbox/ipam/forms/bulk_import.py:304 netbox/ipam/forms/bulk_import.py:505 #: netbox/ipam/forms/bulk_import.py:304 netbox/ipam/forms/bulk_import.py:505
#: netbox/ipam/forms/filtersets.py:551 netbox/ipam/forms/model_forms.py:323 #: netbox/ipam/forms/filtersets.py:551 netbox/ipam/forms/model_forms.py:323
#: netbox/ipam/forms/model_forms.py:712 netbox/ipam/forms/model_forms.py:745 #: netbox/ipam/forms/model_forms.py:712 netbox/ipam/forms/model_forms.py:745
@@ -4087,11 +4088,11 @@ msgstr "Označené VLAN"
#: netbox/dcim/forms/bulk_edit.py:1511 #: netbox/dcim/forms/bulk_edit.py:1511
msgid "Add tagged VLANs" msgid "Add tagged VLANs"
msgstr "" msgstr "Přidat označené VLANy"
#: netbox/dcim/forms/bulk_edit.py:1520 #: netbox/dcim/forms/bulk_edit.py:1520
msgid "Remove tagged VLANs" msgid "Remove tagged VLANs"
msgstr "" msgstr "Odstranit označené VLANy"
#: netbox/dcim/forms/bulk_edit.py:1536 netbox/dcim/forms/model_forms.py:1348 #: netbox/dcim/forms/bulk_edit.py:1536 netbox/dcim/forms/model_forms.py:1348
msgid "Wireless LAN group" msgid "Wireless LAN group"
@@ -4139,7 +4140,7 @@ msgstr "Přepínání 802.1Q"
#: netbox/dcim/forms/bulk_edit.py:1558 #: netbox/dcim/forms/bulk_edit.py:1558
msgid "Add/Remove" msgid "Add/Remove"
msgstr "" msgstr "Přidat/Odebrat"
#: netbox/dcim/forms/bulk_edit.py:1617 netbox/dcim/forms/bulk_edit.py:1619 #: netbox/dcim/forms/bulk_edit.py:1617 netbox/dcim/forms/bulk_edit.py:1619
msgid "Interface mode must be specified to assign VLANs" msgid "Interface mode must be specified to assign VLANs"
@@ -4217,7 +4218,7 @@ msgstr "Název přiřazené role"
#: netbox/dcim/forms/bulk_import.py:264 #: netbox/dcim/forms/bulk_import.py:264
msgid "Rack type model" msgid "Rack type model"
msgstr "" msgstr "Model typu stojanu"
#: netbox/dcim/forms/bulk_import.py:292 netbox/dcim/forms/bulk_import.py:435 #: netbox/dcim/forms/bulk_import.py:292 netbox/dcim/forms/bulk_import.py:435
#: netbox/dcim/forms/bulk_import.py:605 #: netbox/dcim/forms/bulk_import.py:605
@@ -4226,7 +4227,7 @@ msgstr "Směr proudění vzduchu"
#: netbox/dcim/forms/bulk_import.py:324 #: netbox/dcim/forms/bulk_import.py:324
msgid "Width must be set if not specifying a rack type." msgid "Width must be set if not specifying a rack type."
msgstr "" msgstr "Šířka musí být nastavena, pokud není zadán typ stojanu."
#: netbox/dcim/forms/bulk_import.py:326 #: netbox/dcim/forms/bulk_import.py:326
msgid "U height must be set if not specifying a rack type." msgid "U height must be set if not specifying a rack type."
@@ -6768,7 +6769,7 @@ msgstr "Modulové pozice"
msgid "Inventory items" msgid "Inventory items"
msgstr "Inventární položky" msgstr "Inventární položky"
#: netbox/dcim/tables/devices.py:305 netbox/dcim/tables/modules.py:56 #: netbox/dcim/tables/devices.py:305 netbox/dcim/tables/modules.py:57
#: netbox/templates/dcim/modulebay.html:17 #: netbox/templates/dcim/modulebay.html:17
msgid "Module Bay" msgid "Module Bay"
msgstr "Modulová přihrádka" msgstr "Modulová přihrádka"
@@ -7490,12 +7491,12 @@ msgstr "Záložky"
msgid "Show your personal bookmarks" msgid "Show your personal bookmarks"
msgstr "Zobrazit své osobní záložky" msgstr "Zobrazit své osobní záložky"
#: netbox/extras/events.py:147 #: netbox/extras/events.py:151
#, python-brace-format #, python-brace-format
msgid "Unknown action type for an event rule: {action_type}" msgid "Unknown action type for an event rule: {action_type}"
msgstr "Neznámý typ akce pro pravidlo události: {action_type}" msgstr "Neznámý typ akce pro pravidlo události: {action_type}"
#: netbox/extras/events.py:192 #: netbox/extras/events.py:196
#, python-brace-format #, python-brace-format
msgid "Cannot import events pipeline {name} error: {error}" msgid "Cannot import events pipeline {name} error: {error}"
msgstr "Nelze importovat kanál událostí {name} chyba: {error}" msgstr "Nelze importovat kanál událostí {name} chyba: {error}"
@@ -9248,129 +9249,129 @@ msgstr "Export L2VPN"
msgid "Exporting L2VPN (identifier)" msgid "Exporting L2VPN (identifier)"
msgstr "Export L2VPN (identifikátor)" msgstr "Export L2VPN (identifikátor)"
#: netbox/ipam/filtersets.py:155 netbox/ipam/filtersets.py:281 #: netbox/ipam/filtersets.py:155 netbox/ipam/filtersets.py:283
#: netbox/ipam/forms/model_forms.py:229 netbox/ipam/tables/ip.py:212 #: netbox/ipam/forms/model_forms.py:229 netbox/ipam/tables/ip.py:212
#: netbox/templates/ipam/prefix.html:12 #: netbox/templates/ipam/prefix.html:12
msgid "Prefix" msgid "Prefix"
msgstr "Předpona" msgstr "Předpona"
#: netbox/ipam/filtersets.py:159 netbox/ipam/filtersets.py:198 #: netbox/ipam/filtersets.py:159 netbox/ipam/filtersets.py:198
#: netbox/ipam/filtersets.py:221 #: netbox/ipam/filtersets.py:223
msgid "RIR (ID)" msgid "RIR (ID)"
msgstr "RIR (ID)" msgstr "RIR (ID)"
#: netbox/ipam/filtersets.py:165 netbox/ipam/filtersets.py:204 #: netbox/ipam/filtersets.py:165 netbox/ipam/filtersets.py:204
#: netbox/ipam/filtersets.py:227 #: netbox/ipam/filtersets.py:229
msgid "RIR (slug)" msgid "RIR (slug)"
msgstr "RIR (slug)" msgstr "RIR (slug)"
#: netbox/ipam/filtersets.py:285 #: netbox/ipam/filtersets.py:287
msgid "Within prefix" msgid "Within prefix"
msgstr "V rámci předpony" msgstr "V rámci předpony"
#: netbox/ipam/filtersets.py:289 #: netbox/ipam/filtersets.py:291
msgid "Within and including prefix" msgid "Within and including prefix"
msgstr "V rámci a včetně prefixu" msgstr "V rámci a včetně prefixu"
#: netbox/ipam/filtersets.py:293 #: netbox/ipam/filtersets.py:295
msgid "Prefixes which contain this prefix or IP" msgid "Prefixes which contain this prefix or IP"
msgstr "Předpony, které obsahují tuto předponu nebo IP" msgstr "Předpony, které obsahují tuto předponu nebo IP"
#: netbox/ipam/filtersets.py:304 netbox/ipam/filtersets.py:572 #: netbox/ipam/filtersets.py:306 netbox/ipam/filtersets.py:574
#: netbox/ipam/forms/bulk_edit.py:343 netbox/ipam/forms/filtersets.py:196 #: netbox/ipam/forms/bulk_edit.py:343 netbox/ipam/forms/filtersets.py:196
#: netbox/ipam/forms/filtersets.py:331 #: netbox/ipam/forms/filtersets.py:331
msgid "Mask length" msgid "Mask length"
msgstr "Délka masky" msgstr "Délka masky"
#: netbox/ipam/filtersets.py:373 netbox/vpn/filtersets.py:427 #: netbox/ipam/filtersets.py:375 netbox/vpn/filtersets.py:427
msgid "VLAN (ID)" msgid "VLAN (ID)"
msgstr "VLAN (ID)" msgstr "VLAN (ID)"
#: netbox/ipam/filtersets.py:377 netbox/vpn/filtersets.py:422 #: netbox/ipam/filtersets.py:379 netbox/vpn/filtersets.py:422
msgid "VLAN number (1-4094)" msgid "VLAN number (1-4094)"
msgstr "Číslo VLAN (1-4094)" msgstr "Číslo VLAN (1-4094)"
#: netbox/ipam/filtersets.py:471 netbox/ipam/filtersets.py:475 #: netbox/ipam/filtersets.py:473 netbox/ipam/filtersets.py:477
#: netbox/ipam/filtersets.py:567 netbox/ipam/forms/model_forms.py:496 #: netbox/ipam/filtersets.py:569 netbox/ipam/forms/model_forms.py:496
#: netbox/templates/tenancy/contact.html:53 #: netbox/templates/tenancy/contact.html:53
#: netbox/tenancy/forms/bulk_edit.py:113 #: netbox/tenancy/forms/bulk_edit.py:113
msgid "Address" msgid "Address"
msgstr "Adresa" msgstr "Adresa"
#: netbox/ipam/filtersets.py:479 #: netbox/ipam/filtersets.py:481
msgid "Ranges which contain this prefix or IP" msgid "Ranges which contain this prefix or IP"
msgstr "Rozsahy, které obsahují tuto předponu nebo IP" msgstr "Rozsahy, které obsahují tuto předponu nebo IP"
#: netbox/ipam/filtersets.py:507 netbox/ipam/filtersets.py:563 #: netbox/ipam/filtersets.py:509 netbox/ipam/filtersets.py:565
msgid "Parent prefix" msgid "Parent prefix"
msgstr "Nadřazená předpona" msgstr "Nadřazená předpona"
#: netbox/ipam/filtersets.py:616 netbox/ipam/filtersets.py:856 #: netbox/ipam/filtersets.py:618 netbox/ipam/filtersets.py:858
#: netbox/ipam/filtersets.py:1131 netbox/vpn/filtersets.py:385 #: netbox/ipam/filtersets.py:1133 netbox/vpn/filtersets.py:385
msgid "Virtual machine (name)" msgid "Virtual machine (name)"
msgstr "Virtuální počítač (název)" msgstr "Virtuální počítač (název)"
#: netbox/ipam/filtersets.py:621 netbox/ipam/filtersets.py:861 #: netbox/ipam/filtersets.py:623 netbox/ipam/filtersets.py:863
#: netbox/ipam/filtersets.py:1125 netbox/virtualization/filtersets.py:282 #: netbox/ipam/filtersets.py:1127 netbox/virtualization/filtersets.py:282
#: netbox/virtualization/filtersets.py:321 netbox/vpn/filtersets.py:390 #: netbox/virtualization/filtersets.py:321 netbox/vpn/filtersets.py:390
msgid "Virtual machine (ID)" msgid "Virtual machine (ID)"
msgstr "Virtuální počítač (ID)" msgstr "Virtuální počítač (ID)"
#: netbox/ipam/filtersets.py:627 netbox/vpn/filtersets.py:97 #: netbox/ipam/filtersets.py:629 netbox/vpn/filtersets.py:97
#: netbox/vpn/filtersets.py:396 #: netbox/vpn/filtersets.py:396
msgid "Interface (name)" msgid "Interface (name)"
msgstr "Rozhraní (název)" msgstr "Rozhraní (název)"
#: netbox/ipam/filtersets.py:638 netbox/vpn/filtersets.py:108 #: netbox/ipam/filtersets.py:640 netbox/vpn/filtersets.py:108
#: netbox/vpn/filtersets.py:407 #: netbox/vpn/filtersets.py:407
msgid "VM interface (name)" msgid "VM interface (name)"
msgstr "Rozhraní virtuálního počítače (název)" msgstr "Rozhraní virtuálního počítače (název)"
#: netbox/ipam/filtersets.py:643 netbox/vpn/filtersets.py:113 #: netbox/ipam/filtersets.py:645 netbox/vpn/filtersets.py:113
msgid "VM interface (ID)" msgid "VM interface (ID)"
msgstr "Rozhraní virtuálního počítače (ID)" msgstr "Rozhraní virtuálního počítače (ID)"
#: netbox/ipam/filtersets.py:648 #: netbox/ipam/filtersets.py:650
msgid "FHRP group (ID)" msgid "FHRP group (ID)"
msgstr "Skupina FHRP (ID)" msgstr "Skupina FHRP (ID)"
#: netbox/ipam/filtersets.py:652 #: netbox/ipam/filtersets.py:654
msgid "Is assigned to an interface" msgid "Is assigned to an interface"
msgstr "Je přiřazen k rozhraní" msgstr "Je přiřazen k rozhraní"
#: netbox/ipam/filtersets.py:656 #: netbox/ipam/filtersets.py:658
msgid "Is assigned" msgid "Is assigned"
msgstr "Je přiřazen" msgstr "Je přiřazen"
#: netbox/ipam/filtersets.py:668 #: netbox/ipam/filtersets.py:670
msgid "Service (ID)" msgid "Service (ID)"
msgstr "Služba (ID)" msgstr "Služba (ID)"
#: netbox/ipam/filtersets.py:673 #: netbox/ipam/filtersets.py:675
msgid "NAT inside IP address (ID)" msgid "NAT inside IP address (ID)"
msgstr "NAT uvnitř IP adresy (ID)" msgstr "NAT uvnitř IP adresy (ID)"
#: netbox/ipam/filtersets.py:1041 netbox/ipam/forms/bulk_import.py:322 #: netbox/ipam/filtersets.py:1043 netbox/ipam/forms/bulk_import.py:322
msgid "Assigned interface" msgid "Assigned interface"
msgstr "Přiřazené rozhraní" msgstr "Přiřazené rozhraní"
#: netbox/ipam/filtersets.py:1046 #: netbox/ipam/filtersets.py:1048
msgid "Assigned VM interface" msgid "Assigned VM interface"
msgstr "Přiřazené rozhraní virtuálního počítače" msgstr "Přiřazené rozhraní virtuálního počítače"
#: netbox/ipam/filtersets.py:1136 #: netbox/ipam/filtersets.py:1138
msgid "IP address (ID)" msgid "IP address (ID)"
msgstr "IP adresa (ID)" msgstr "IP adresa (ID)"
#: netbox/ipam/filtersets.py:1142 netbox/ipam/models/ip.py:788 #: netbox/ipam/filtersets.py:1144 netbox/ipam/models/ip.py:788
msgid "IP address" msgid "IP address"
msgstr "IP adresa" msgstr "IP adresa"
#: netbox/ipam/filtersets.py:1167 #: netbox/ipam/filtersets.py:1169
msgid "Primary IPv4 (ID)" msgid "Primary IPv4 (ID)"
msgstr "Primární IPv4 (ID)" msgstr "Primární IPv4 (ID)"
#: netbox/ipam/filtersets.py:1172 #: netbox/ipam/filtersets.py:1174
msgid "Primary IPv6 (ID)" msgid "Primary IPv6 (ID)"
msgstr "Primární IPv6 (ID)" msgstr "Primární IPv6 (ID)"
@@ -12582,11 +12583,11 @@ msgstr "Ke stažení"
#: netbox/templates/dcim/device/render_config.html:64 #: netbox/templates/dcim/device/render_config.html:64
#: netbox/templates/virtualization/virtualmachine/render_config.html:64 #: netbox/templates/virtualization/virtualmachine/render_config.html:64
msgid "Error rendering template" msgid "Error rendering template"
msgstr "" msgstr "Chyba při vykreslování šablony"
#: netbox/templates/dcim/device/render_config.html:70 #: netbox/templates/dcim/device/render_config.html:70
msgid "No configuration template has been assigned for this device." msgid "No configuration template has been assigned for this device."
msgstr "" msgstr "Pro toto zařízení nebyla přiřazena žádná konfigurační šablona."
#: netbox/templates/dcim/device_edit.html:44 #: netbox/templates/dcim/device_edit.html:44
msgid "Parent Bay" msgid "Parent Bay"
@@ -13452,7 +13453,7 @@ msgstr "Spustit znovu"
#: netbox/templates/extras/script_list.html:133 #: netbox/templates/extras/script_list.html:133
#, python-format #, python-format
msgid "Could not load scripts from module %(module)s" msgid "Could not load scripts from module %(module)s"
msgstr "" msgstr "Nelze načíst skripty z modulu %(module)s"
#: netbox/templates/extras/script_list.html:141 #: netbox/templates/extras/script_list.html:141
msgid "No Scripts Found" msgid "No Scripts Found"
@@ -14265,6 +14266,7 @@ msgstr "Přidat virtuální disk"
#: netbox/templates/virtualization/virtualmachine/render_config.html:70 #: netbox/templates/virtualization/virtualmachine/render_config.html:70
msgid "No configuration template has been assigned for this virtual machine." msgid "No configuration template has been assigned for this virtual machine."
msgstr "" msgstr ""
"Pro tento virtuální počítač nebyla přiřazena žádná konfigurační šablona."
#: netbox/templates/vpn/ikepolicy.html:10 #: netbox/templates/vpn/ikepolicy.html:10
#: netbox/templates/vpn/ipsecprofile.html:33 netbox/vpn/tables/crypto.py:166 #: netbox/templates/vpn/ipsecprofile.html:33 netbox/vpn/tables/crypto.py:166
@@ -15330,12 +15332,12 @@ msgstr "Paměť (MB)"
#: netbox/virtualization/forms/bulk_edit.py:174 #: netbox/virtualization/forms/bulk_edit.py:174
msgid "Disk (MB)" msgid "Disk (MB)"
msgstr "" msgstr "Disk (MB)"
#: netbox/virtualization/forms/bulk_edit.py:334 #: netbox/virtualization/forms/bulk_edit.py:334
#: netbox/virtualization/forms/filtersets.py:251 #: netbox/virtualization/forms/filtersets.py:251
msgid "Size (MB)" msgid "Size (MB)"
msgstr "" msgstr "Velikost (MB)"
#: netbox/virtualization/forms/bulk_import.py:44 #: netbox/virtualization/forms/bulk_import.py:44
msgid "Type of cluster" msgid "Type of cluster"
@@ -15544,19 +15546,19 @@ msgstr "GREE"
#: netbox/vpn/choices.py:39 #: netbox/vpn/choices.py:39
msgid "WireGuard" msgid "WireGuard"
msgstr "" msgstr "WireGuard"
#: netbox/vpn/choices.py:40 #: netbox/vpn/choices.py:40
msgid "OpenVPN" msgid "OpenVPN"
msgstr "" msgstr "OpenVPN"
#: netbox/vpn/choices.py:41 #: netbox/vpn/choices.py:41
msgid "L2TP" msgid "L2TP"
msgstr "" msgstr "L2TP"
#: netbox/vpn/choices.py:42 #: netbox/vpn/choices.py:42
msgid "PPTP" msgid "PPTP"
msgstr "" msgstr "PPTP"
#: netbox/vpn/choices.py:64 #: netbox/vpn/choices.py:64
msgid "Hub" msgid "Hub"

File diff suppressed because it is too large Load Diff

View File

@@ -12,8 +12,8 @@
# Alexander Ryazanov (alryaz) <alryaz@xavux.com>, 2024 # Alexander Ryazanov (alryaz) <alryaz@xavux.com>, 2024
# Vladyslav V. Prodan, 2024 # Vladyslav V. Prodan, 2024
# Jeremy Stretch, 2024 # Jeremy Stretch, 2024
# Michail Tatarinov, 2024
# Artem Kotik, 2025 # Artem Kotik, 2025
# Michail Tatarinov, 2025
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@@ -22,7 +22,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-04 05:02+0000\n" "POT-Creation-Date: 2025-01-04 05:02+0000\n"
"PO-Revision-Date: 2023-10-30 17:48+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n"
"Last-Translator: Artem Kotik, 2025\n" "Last-Translator: Michail Tatarinov, 2025\n"
"Language-Team: Russian (https://app.transifex.com/netbox-community/teams/178115/ru/)\n" "Language-Team: Russian (https://app.transifex.com/netbox-community/teams/178115/ru/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -6334,7 +6334,7 @@ msgstr "распределительный щит"
#: netbox/dcim/models/power.py:56 #: netbox/dcim/models/power.py:56
msgid "power panels" msgid "power panels"
msgstr "распределительный щиты" msgstr "распределительные щиты"
#: netbox/dcim/models/power.py:70 #: netbox/dcim/models/power.py:70
#, python-brace-format #, python-brace-format
@@ -9256,7 +9256,7 @@ msgstr "SLAAC"
#: netbox/ipam/choices.py:89 #: netbox/ipam/choices.py:89
msgid "Loopback" msgid "Loopback"
msgstr "Обратная петля" msgstr "Loopback"
#: netbox/ipam/choices.py:91 #: netbox/ipam/choices.py:91
msgid "Anycast" msgid "Anycast"
@@ -11185,7 +11185,7 @@ msgstr "Сети провайдеров"
#: netbox/netbox/navigation/menu.py:298 #: netbox/netbox/navigation/menu.py:298
msgid "Power Panels" msgid "Power Panels"
msgstr "Распределительный щиты" msgstr "Распределительные щиты"
#: netbox/netbox/navigation/menu.py:309 #: netbox/netbox/navigation/menu.py:309
msgid "Configurations" msgid "Configurations"

View File

@@ -1,4 +1,5 @@
from django import forms from django import forms
from django.apps import apps
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -143,17 +144,24 @@ class ClusterAddDevicesForm(forms.Form):
def clean(self): def clean(self):
super().clean() super().clean()
# If the Cluster is assigned to a Site, all Devices must be assigned to that Site. # If the Cluster is assigned to a Site or Location, all Devices must be assigned to that same scope.
if self.cluster.site is not None: if self.cluster.scope is not None:
for device in self.cleaned_data.get('devices', []): for device in self.cleaned_data.get('devices', []):
if device.site != self.cluster.site: for scope_field in ['site', 'location']:
device_scope = getattr(device, scope_field)
if (
self.cluster.scope_type.model_class() == apps.get_model('dcim', scope_field)
and device_scope != self.cluster.scope
):
raise ValidationError({ raise ValidationError({
'devices': _( 'devices': _(
"{device} belongs to a different site ({device_site}) than the cluster ({cluster_site})" "{device} belongs to a different {scope_field} ({device_scope}) than the "
"cluster ({cluster_scope})"
).format( ).format(
device=device, device=device,
device_site=device.site, scope_field=scope_field,
cluster_site=self.cluster.site device_scope=device_scope,
cluster_scope=self.cluster.scope
) )
}) })

View File

@@ -10,7 +10,7 @@ class ClusterIndex(SearchIndex):
('description', 500), ('description', 500),
('comments', 5000), ('comments', 5000),
) )
display_attrs = ('type', 'group', 'status', 'tenant', 'site', 'description') display_attrs = ('type', 'group', 'status', 'tenant', 'scope', 'description')
@register_search @register_search

View File

@@ -78,7 +78,8 @@ class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
) )
scope = tables.Column( scope = tables.Column(
verbose_name=_('Scope'), verbose_name=_('Scope'),
linkify=True linkify=True,
orderable=False
) )
device_count = columns.LinkedCountColumn( device_count = columns.LinkedCountColumn(
viewname='dcim:device_list', viewname='dcim:device_list',

View File

@@ -11,7 +11,7 @@ class WirelessLANIndex(SearchIndex):
('auth_psk', 2000), ('auth_psk', 2000),
('comments', 5000), ('comments', 5000),
) )
display_attrs = ('group', 'status', 'vlan', 'tenant', 'description') display_attrs = ('group', 'status', 'vlan', 'tenant', 'scope', 'description')
@register_search @register_search

View File

@@ -56,7 +56,8 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
) )
scope = tables.Column( scope = tables.Column(
verbose_name=_('Scope'), verbose_name=_('Scope'),
linkify=True linkify=True,
orderable=False
) )
interface_count = tables.Column( interface_count = tables.Column(
verbose_name=_('Interfaces') verbose_name=_('Interfaces')

View File

@@ -1,6 +1,6 @@
Django==5.1.4 Django==5.1.5
django-cors-headers==4.6.0 django-cors-headers==4.6.0
django-debug-toolbar==4.4.6 django-debug-toolbar==5.0.1
django-filter==24.3 django-filter==24.3
django-htmx==1.21.0 django-htmx==1.21.0
django-graphiql-debug-toolbar==0.2.0 django-graphiql-debug-toolbar==0.2.0
@@ -12,7 +12,7 @@ django-rich==1.13.0
django-rq==3.0 django-rq==3.0
django-taggit==6.1.0 django-taggit==6.1.0
django-tables2==2.7.5 django-tables2==2.7.5
django-timezone-field==7.0 django-timezone-field==7.1
djangorestframework==3.15.2 djangorestframework==3.15.2
drf-spectacular==0.28.0 drf-spectacular==0.28.0
drf-spectacular-sidecar==2024.12.1 drf-spectacular-sidecar==2024.12.1
@@ -25,14 +25,14 @@ mkdocstrings[python-legacy]==0.27.0
netaddr==1.3.0 netaddr==1.3.0
nh3==0.2.20 nh3==0.2.20
Pillow==11.1.0 Pillow==11.1.0
psycopg[c,pool]==3.2.3 psycopg[c,pool]==3.2.4
PyYAML==6.0.2 PyYAML==6.0.2
requests==2.32.3 requests==2.32.3
rq==2.1.0 rq==2.1.0
social-auth-app-django==5.4.2 social-auth-app-django==5.4.2
social-auth-core==4.5.4 social-auth-core==4.5.4
strawberry-graphql==0.256.1 strawberry-graphql==0.258.0
strawberry-graphql-django==0.53.1 strawberry-graphql-django==0.52.0
svgwrite==1.4.3 svgwrite==1.4.3
tablib==3.7.0 tablib==3.7.0
tzdata==2024.2 tzdata==2024.2

View File

@@ -1,11 +1,6 @@
#!/bin/sh #!/bin/sh
# Create a link to this file at .git/hooks/pre-commit to # TODO: Remove this file in NetBox v4.3
# force PEP8 validation prior to committing # This script has been maintained to ease transition to the pre-commit tool.
#
# Ignored violations:
#
# W504: Line break after binary operator
# E501: Line too long
exec 1>&2 exec 1>&2
@@ -14,48 +9,8 @@ RED='\033[0;31m'
YELLOW='\033[0;33m' YELLOW='\033[0;33m'
NOCOLOR='\033[0m' NOCOLOR='\033[0m'
printf "${YELLOW}This script is obsolete and will be removed in a future release.\n" printf "${YELLOW}The pre-commit hook script is obsolete. Please use pre-commit instead:${NOCOLOR}\n"
printf "Please use pre-commit instead:\n"
printf " pip install pre-commit\n" printf " pip install pre-commit\n"
printf " pre-commit install${NOCOLOR}\n" printf " pre-commit install${NOCOLOR}\n"
if [ -d ./venv/ ]; then exit 1
VENV="$PWD/venv"
if [ -e $VENV/bin/python ]; then
PATH=$VENV/bin:$PATH
elif [ -e $VENV/Scripts/python.exe ]; then
PATH=$VENV/Scripts:$PATH
fi
fi
if [ ${NOVALIDATE} ]; then
echo "${YELLOW}Skipping validation checks${NOCOLOR}"
exit $EXIT
fi
echo "Linting with ruff..."
ruff check netbox/
if [ $? != 0 ]; then
EXIT=1
fi
echo "Checking for missing migrations..."
python netbox/manage.py makemigrations --dry-run --check
if [ $? != 0 ]; then
EXIT=1
fi
git diff --cached --name-only | if grep --quiet 'netbox/project-static/'
then
echo "Checking UI ESLint, TypeScript, and Prettier compliance..."
yarn --cwd "$PWD/netbox/project-static" validate
if [ $? != 0 ]; then
EXIT=1
fi
fi
if [ $EXIT != 0 ]; then
printf "${RED}COMMIT FAILED${NOCOLOR}\n"
fi
exit $EXIT