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:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v4.2.0
placeholder: v4.2.2
validations:
required: true
- type: dropdown
@@ -27,19 +27,6 @@ body:
- Other
validations:
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
attributes:
label: Proposed functionality

View File

@@ -22,24 +22,11 @@ body:
- Self-hosted
validations:
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
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v4.2.0
placeholder: v4.2.2
validations:
required: true
- type: dropdown

View File

@@ -8,8 +8,6 @@ django-cors-headers
# Runtime UI tool for debugging Django
# 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
# Library for writing reusable URL query filters
@@ -134,7 +132,8 @@ strawberry-graphql
# Strawberry GraphQL Django extension
# 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)
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst

View File

@@ -25,7 +25,7 @@ ALLOWED_HOSTS = ['*']
## 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
* `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).
!!! warning "PostgreSQL 12 or later required"
NetBox requires PostgreSQL 12 or later. Please note that MySQL and other relational databases are **not** supported.
!!! warning "PostgreSQL 13 or later required"
NetBox requires PostgreSQL 13 or later. Please note that MySQL and other relational databases are **not** supported.
## Installation
@@ -34,7 +34,7 @@ This section entails the installation and configuration of a local PostgreSQL da
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
psql -V

View File

@@ -21,7 +21,7 @@ The following sections detail how to set up a new instance of NetBox:
| Dependency | Supported Versions |
|------------|--------------------|
| Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 12+ |
| PostgreSQL | 13+ |
| Redis | 4.0+ |
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 |
|------------|--------------------|
| Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 12+ |
| PostgreSQL | 13+ |
| Redis | 4.0+ |
## 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 |
| WSGI service | gunicorn or uWSGI |
| Application | Django/Python |
| Database | PostgreSQL 12+ |
| Database | PostgreSQL 13+ |
| Task queuing | Redis/django-rq |

View File

@@ -1,10 +1,44 @@
# 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)
### Breaking Changes
* 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.
* 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.

View File

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

View File

@@ -1,8 +1,11 @@
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 .choices import DataSourceStatusChoices
from .choices import DataSourceStatusChoices, JobIntervalChoices
from .exceptions import SyncError
from .models import DataSource
@@ -31,3 +34,44 @@ class SyncDataSourceJob(JobRunner):
if type(e) is SyncError:
logging.error(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
# Serialize any CustomValidator classes
if hasattr(config, 'CUSTOM_VALIDATORS') and config.CUSTOM_VALIDATORS:
config.CUSTOM_VALIDATORS = json.dumps(config.CUSTOM_VALIDATORS, cls=ConfigJSONEncoder, indent=4)
for attr in ['CUSTOM_VALIDATORS', 'PROTECTION_RULES']:
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', {
'stats': stats,
@@ -594,7 +595,7 @@ class BasePluginView(UserPassesTestMixin, View):
catalog_plugins_error = cache.get(self.CACHE_KEY_CATALOG_ERROR, default=False)
if not catalog_plugins_error:
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.set(self.CACHE_KEY_CATALOG_ERROR, True, 300)
messages.warning(request, _("Plugins catalog could not be loaded"))

View File

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

View File

@@ -374,22 +374,27 @@ class Rack(ContactsMixin, ImageAttachmentsMixin, RackBase):
if not self._state.adding:
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
if top_device := mounted_devices.last():
min_height = top_device.position + top_device.device_type.u_height - self.starting_unit
if self.u_height < min_height:
min_height = top_device.position + top_device.device_type.u_height - effective_starting_unit
if effective_u_height < min_height:
field = 'rack_type' if self.rack_type else 'u_height'
raise ValidationError({
'u_height': _(
field: _(
"Rack must be at least {min_height}U tall to house currently installed devices."
).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
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({
'starting_unit': _("Rack unit numbering must begin at {position} or less to house "
"currently installed devices.").format(position=last_device.position)
field: _("Rack unit numbering must begin at {position} or less to house "
"currently installed devices.").format(position=last_device.position)
})
# Validate that Rack was assigned a Location of its same site, if applicable

View File

@@ -69,16 +69,18 @@ INTERFACE_FHRPGROUPS = """
"""
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 %}
<a href="{% url 'ipam:vlan_list' %}?{{ record|meta:"model_name" }}_id={{ record.pk }}">{{ value.count }} VLANs</a>
{% else %}
{% for vlan in value.all %}
<a href="{{ vlan.get_absolute_url }}">{{ vlan }}</a><br />
<a href="{{ vlan.get_absolute_url }}">{{ vlan }}</a><br />
{% endfor %}
{% endif %}
{% elif record.mode == 'tagged-all' %}
All
{% endif %}
"""

View File

@@ -2447,3 +2447,46 @@ class VirtualDeviceContextTest(APIViewTestCases.APIViewTestCase):
# 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 = {
'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 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 netbox.constants import DEFAULT_ACTION_PERMISSIONS
from netbox.views import generic
@@ -30,8 +30,9 @@ from utilities.views import (
)
from virtualization.filtersets import VirtualMachineFilterSet
from virtualization.forms import VirtualMachineFilterForm
from virtualization.models import VirtualMachine
from virtualization.models import Cluster, VirtualMachine
from virtualization.tables import VirtualMachineTable
from wireless.models import WirelessLAN
from . import filtersets, forms, tables
from .choices import DeviceFaceChoices, InterfaceModeChoices
from .models import *
@@ -238,6 +239,7 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView):
'related_models': self.get_related_models(
request,
regions,
omit=(Cluster, Prefix, WirelessLAN),
extra=(
(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'),
@@ -247,6 +249,11 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView):
).distinct(),
'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(
request,
groups,
omit=(Cluster, Prefix, WirelessLAN),
extra=(
(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'),
@@ -340,6 +348,20 @@ class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
).distinct(),
'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(
request,
instance,
[CableTermination, CircuitTermination],
(
omit=(CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN),
extra=(
(VLANGroup.objects.restrict(request.user, 'view').filter(
scope_type=ContentType.objects.get_for_model(Site),
scope_id=instance.pk
@@ -429,6 +451,11 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView):
Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(),
'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(
request,
locations,
[CableTermination],
(
omit=[CableTermination, Cluster, Prefix, WirelessLAN],
extra=(
(
Circuit.objects.restrict(request.user, 'view').filter(
terminations___location=instance
).distinct(),
'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}'
def get_feed(self):
if self.config['requires_internet'] and settings.ISOLATED_DEPLOYMENT:
if self.config.get('requires_internet') and settings.ISOLATED_DEPLOYMENT:
return {
'isolated_deployment': True,
}

View File

@@ -120,11 +120,12 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
is_active=True,
)
# Apply Location & DeviceType filters only for VirtualMachines
if self.model._meta.model_name == 'device':
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)
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(roles=OuterRef('role')) | Q(roles=None)), Q.AND)

View File

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

View File

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

View File

@@ -5,9 +5,7 @@ import os
import platform
import sys
import warnings
from urllib.parse import urlencode
import requests
from django.contrib.messages import constants as messages
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.validators import URLValidator
@@ -224,8 +222,18 @@ DATABASES = {
# 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:
DEFAULT_FILE_STORAGE = STORAGE_BACKEND
STORAGES['default']['BACKEND'] = STORAGE_BACKEND
# django-storages
if STORAGE_BACKEND.startswith('storages.'):
@@ -583,17 +591,6 @@ if SENTRY_ENABLED:
# Calculate a unique deployment ID from the secret key
DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16]
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",
"clipboard": "2.0.11",
"flatpickr": "4.6.13",
"gridstack": "11.2.0",
"gridstack": "11.3.0",
"htmx.org": "1.9.12",
"query-string": "9.1.1",
"sass": "1.83.1",
"sass": "1.83.4",
"tom-select": "2.4.1",
"typeface-inter": "3.18.1",
"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"
integrity sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==
gridstack@11.2.0:
version "11.2.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-11.2.0.tgz#8977a6632c521260f064ef171b92c7a8df4f58a9"
integrity sha512-ajwUzd9spR8NXDxfJotHWq9WOYoDOV9o6UJR3ksevNz8cvXNxDtI9H/lC+RN6ijM2DexureLlsG0RpYjBZiOtg==
gridstack@11.3.0:
version "11.3.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-11.3.0.tgz#b110c66bafc64c920fc54933e2c9df4f7b2cfffe"
integrity sha512-Z0eRovKcZTRTs3zetJwjO6CNwrgIy845WfOeZGk8ybpeMCE8fMA8tScyKU72Y2M6uGHkjgwnjflglvPiv+RcBQ==
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
@@ -2667,10 +2667,10 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0"
is-regex "^1.1.4"
sass@1.83.1:
version "1.83.1"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.1.tgz#dee1ab94b47a6f9993d3195d36f556bcbda64846"
integrity sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==
sass@1.83.4:
version "1.83.4"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.4.tgz#5ccf60f43eb61eeec300b780b8dcb85f16eec6d1"
integrity sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==
dependencies:
chokidar "^4.0.0"
immutable "^5.0.2"

View File

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

View File

@@ -103,7 +103,7 @@
<tr>
<th scope="row" class="border-0 ps-3">{% trans "Protection rules" %}</th>
{% 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 %}
<td class="border-0">{{ ''|placeholder }}</td>
{% 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.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 %}
{% block extra_controls %}

View File

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

View File

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

View File

@@ -7,15 +7,16 @@
# czarnian, 2024
# Jeremy Stretch, 2024
# Pavel Valach, 2024
# Matěj Gordon, 2025
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"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"
"MIME-Version: 1.0\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:1368 netbox/dcim/filtersets.py:1903
#: 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:173 netbox/vpn/filtersets.py:358
msgid "Region (ID)"
@@ -163,8 +164,8 @@ msgstr "Region (ID)"
#: netbox/dcim/filtersets.py:471 netbox/dcim/filtersets.py:1028
#: netbox/dcim/filtersets.py:1375 netbox/dcim/filtersets.py:1910
#: netbox/dcim/filtersets.py:2153 netbox/dcim/filtersets.py:2211
#: netbox/extras/filtersets.py:509 netbox/ipam/filtersets.py:346
#: netbox/ipam/filtersets.py:966 netbox/virtualization/filtersets.py:52
#: netbox/extras/filtersets.py:509 netbox/ipam/filtersets.py:348
#: netbox/ipam/filtersets.py:968 netbox/virtualization/filtersets.py:52
#: netbox/virtualization/filtersets.py:180 netbox/vpn/filtersets.py:353
msgid "Region (slug)"
msgstr "Region (zkratka)"
@@ -174,8 +175,8 @@ msgstr "Region (zkratka)"
#: netbox/dcim/filtersets.py:346 netbox/dcim/filtersets.py:477
#: netbox/dcim/filtersets.py:1034 netbox/dcim/filtersets.py:1381
#: netbox/dcim/filtersets.py:1916 netbox/dcim/filtersets.py:2159
#: netbox/dcim/filtersets.py:2217 netbox/ipam/filtersets.py:352
#: netbox/ipam/filtersets.py:972 netbox/virtualization/filtersets.py:58
#: netbox/dcim/filtersets.py:2217 netbox/ipam/filtersets.py:354
#: netbox/ipam/filtersets.py:974 netbox/virtualization/filtersets.py:58
#: netbox/virtualization/filtersets.py:186
msgid "Site group (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:1923 netbox/dcim/filtersets.py:2166
#: 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:193
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:274 netbox/dcim/filtersets.py:242
#: netbox/dcim/filtersets.py:363 netbox/dcim/filtersets.py:458
#: netbox/extras/filtersets.py:531 netbox/ipam/filtersets.py:238
#: netbox/ipam/filtersets.py:369 netbox/ipam/filtersets.py:989
#: netbox/extras/filtersets.py:531 netbox/ipam/filtersets.py:240
#: netbox/ipam/filtersets.py:371 netbox/ipam/filtersets.py:991
#: netbox/virtualization/filtersets.py:75
#: netbox/virtualization/filtersets.py:203 netbox/vpn/filtersets.py:363
msgid "Site (slug)"
@@ -276,13 +277,13 @@ msgstr "ASN"
#: netbox/circuits/filtersets.py:95 netbox/circuits/filtersets.py:122
#: 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)"
msgstr "Poskytovatel (ID)"
#: netbox/circuits/filtersets.py:101 netbox/circuits/filtersets.py:128
#: 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)"
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:1393 netbox/dcim/filtersets.py:1928
#: netbox/dcim/filtersets.py:2170 netbox/dcim/filtersets.py:2229
#: netbox/ipam/filtersets.py:232 netbox/ipam/filtersets.py:363
#: netbox/ipam/filtersets.py:983 netbox/virtualization/filtersets.py:69
#: netbox/ipam/filtersets.py:234 netbox/ipam/filtersets.py:365
#: netbox/ipam/filtersets.py:985 netbox/virtualization/filtersets.py:69
#: netbox/virtualization/filtersets.py:197 netbox/vpn/filtersets.py:368
msgid "Site (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:1059 netbox/dcim/forms/filtersets.py:1170
#: 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/sites.py:82 netbox/dcim/tables/sites.py:138
#: 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/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/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/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:122
#: 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:107 netbox/dcim/tables/devices.py:1036
#: 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/racks.py:145 netbox/dcim/tables/racks.py:225
#: 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)"
#: 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)"
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:994 netbox/dcim/filtersets.py:1850
#: netbox/ipam/filtersets.py:381 netbox/ipam/filtersets.py:493
#: netbox/ipam/filtersets.py:1003 netbox/virtualization/filtersets.py:210
#: netbox/ipam/filtersets.py:383 netbox/ipam/filtersets.py:495
#: netbox/ipam/filtersets.py:1005 netbox/virtualization/filtersets.py:210
msgid "Role (ID)"
msgstr "Role (ID)"
#: netbox/dcim/filtersets.py:417 netbox/dcim/filtersets.py:898
#: netbox/dcim/filtersets.py:1000 netbox/dcim/filtersets.py:1856
#: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:387
#: netbox/ipam/filtersets.py:499 netbox/ipam/filtersets.py:1009
#: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:389
#: netbox/ipam/filtersets.py:501 netbox/ipam/filtersets.py:1011
#: netbox/virtualization/filtersets.py:216
msgid "Role (slug)"
msgstr "Role (slug)"
@@ -3196,7 +3197,7 @@ msgstr "VDC (ID)"
msgid "Device model"
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
msgid "Interface (ID)"
msgstr "Rozhraní (ID)"
@@ -3210,8 +3211,8 @@ msgid "Module bay (ID)"
msgstr "Modulová přihrádka (ID)"
#: netbox/dcim/filtersets.py:1333 netbox/dcim/filtersets.py:1425
#: netbox/ipam/filtersets.py:611 netbox/ipam/filtersets.py:851
#: netbox/ipam/filtersets.py:1115 netbox/virtualization/filtersets.py:161
#: netbox/ipam/filtersets.py:613 netbox/ipam/filtersets.py:853
#: netbox/ipam/filtersets.py:1117 netbox/virtualization/filtersets.py:161
#: netbox/vpn/filtersets.py:379
msgid "Device (ID)"
msgstr "Zařízení (ID)"
@@ -3220,8 +3221,8 @@ msgstr "Zařízení (ID)"
msgid "Rack (name)"
msgstr "Stojan (název)"
#: netbox/dcim/filtersets.py:1431 netbox/ipam/filtersets.py:606
#: netbox/ipam/filtersets.py:846 netbox/ipam/filtersets.py:1121
#: netbox/dcim/filtersets.py:1431 netbox/ipam/filtersets.py:608
#: netbox/ipam/filtersets.py:848 netbox/ipam/filtersets.py:1123
#: netbox/vpn/filtersets.py:374
msgid "Device (name)"
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/model_forms.py:1385
#: netbox/dcim/models/device_components.py:711
#: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:316
#: netbox/ipam/filtersets.py:327 netbox/ipam/filtersets.py:483
#: netbox/ipam/filtersets.py:584 netbox/ipam/filtersets.py:595
#: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:318
#: netbox/ipam/filtersets.py:329 netbox/ipam/filtersets.py:485
#: 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:340 netbox/ipam/forms/bulk_import.py:157
#: 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"
msgstr "VRF"
#: netbox/dcim/filtersets.py:1619 netbox/ipam/filtersets.py:322
#: netbox/ipam/filtersets.py:333 netbox/ipam/filtersets.py:489
#: netbox/ipam/filtersets.py:590 netbox/ipam/filtersets.py:601
#: netbox/dcim/filtersets.py:1619 netbox/ipam/filtersets.py:324
#: netbox/ipam/filtersets.py:335 netbox/ipam/filtersets.py:491
#: netbox/ipam/filtersets.py:592 netbox/ipam/filtersets.py:603
msgid "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
msgid "L2VPN (ID)"
msgstr "L2VPN (ID)"
#: 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/templates/dcim/interface.html:93 netbox/templates/ipam/vlan.html:66
#: 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/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/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/templates/dcim/devicetype.html:14
#: netbox/templates/dcim/inventoryitem.html:44
@@ -3727,7 +3728,7 @@ msgid "Device Type"
msgstr "Typ zařízení"
#: 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/modulebay.html:66
#: 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:801 netbox/dcim/tables/devices.py:861
#: 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/filtersets.py:551 netbox/ipam/forms/model_forms.py:323
#: 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
msgid "Add tagged VLANs"
msgstr ""
msgstr "Přidat označené VLANy"
#: netbox/dcim/forms/bulk_edit.py:1520
msgid "Remove tagged VLANs"
msgstr ""
msgstr "Odstranit označené VLANy"
#: netbox/dcim/forms/bulk_edit.py:1536 netbox/dcim/forms/model_forms.py:1348
msgid "Wireless LAN group"
@@ -4139,7 +4140,7 @@ msgstr "Přepínání 802.1Q"
#: netbox/dcim/forms/bulk_edit.py:1558
msgid "Add/Remove"
msgstr ""
msgstr "Přidat/Odebrat"
#: netbox/dcim/forms/bulk_edit.py:1617 netbox/dcim/forms/bulk_edit.py:1619
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
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:605
@@ -4226,7 +4227,7 @@ msgstr "Směr proudění vzduchu"
#: netbox/dcim/forms/bulk_import.py:324
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
msgid "U height must be set if not specifying a rack type."
@@ -6768,7 +6769,7 @@ msgstr "Modulové pozice"
msgid "Inventory items"
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
msgid "Module Bay"
msgstr "Modulová přihrádka"
@@ -7490,12 +7491,12 @@ msgstr "Záložky"
msgid "Show your personal bookmarks"
msgstr "Zobrazit své osobní záložky"
#: netbox/extras/events.py:147
#: netbox/extras/events.py:151
#, python-brace-format
msgid "Unknown action type for an event rule: {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
msgid "Cannot import events pipeline {name} error: {error}"
msgstr "Nelze importovat kanál událostí {name} chyba: {error}"
@@ -9248,129 +9249,129 @@ msgstr "Export L2VPN"
msgid "Exporting L2VPN (identifier)"
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/templates/ipam/prefix.html:12
msgid "Prefix"
msgstr "Předpona"
#: netbox/ipam/filtersets.py:159 netbox/ipam/filtersets.py:198
#: netbox/ipam/filtersets.py:221
#: netbox/ipam/filtersets.py:223
msgid "RIR (ID)"
msgstr "RIR (ID)"
#: netbox/ipam/filtersets.py:165 netbox/ipam/filtersets.py:204
#: netbox/ipam/filtersets.py:227
#: netbox/ipam/filtersets.py:229
msgid "RIR (slug)"
msgstr "RIR (slug)"
#: netbox/ipam/filtersets.py:285
#: netbox/ipam/filtersets.py:287
msgid "Within prefix"
msgstr "V rámci předpony"
#: netbox/ipam/filtersets.py:289
#: netbox/ipam/filtersets.py:291
msgid "Within and including prefix"
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"
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/filtersets.py:331
msgid "Mask length"
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)"
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)"
msgstr "Číslo VLAN (1-4094)"
#: netbox/ipam/filtersets.py:471 netbox/ipam/filtersets.py:475
#: netbox/ipam/filtersets.py:567 netbox/ipam/forms/model_forms.py:496
#: netbox/ipam/filtersets.py:473 netbox/ipam/filtersets.py:477
#: netbox/ipam/filtersets.py:569 netbox/ipam/forms/model_forms.py:496
#: netbox/templates/tenancy/contact.html:53
#: netbox/tenancy/forms/bulk_edit.py:113
msgid "Address"
msgstr "Adresa"
#: netbox/ipam/filtersets.py:479
#: netbox/ipam/filtersets.py:481
msgid "Ranges which contain this prefix or 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"
msgstr "Nadřazená předpona"
#: netbox/ipam/filtersets.py:616 netbox/ipam/filtersets.py:856
#: netbox/ipam/filtersets.py:1131 netbox/vpn/filtersets.py:385
#: netbox/ipam/filtersets.py:618 netbox/ipam/filtersets.py:858
#: netbox/ipam/filtersets.py:1133 netbox/vpn/filtersets.py:385
msgid "Virtual machine (name)"
msgstr "Virtuální počítač (název)"
#: netbox/ipam/filtersets.py:621 netbox/ipam/filtersets.py:861
#: netbox/ipam/filtersets.py:1125 netbox/virtualization/filtersets.py:282
#: netbox/ipam/filtersets.py:623 netbox/ipam/filtersets.py:863
#: netbox/ipam/filtersets.py:1127 netbox/virtualization/filtersets.py:282
#: netbox/virtualization/filtersets.py:321 netbox/vpn/filtersets.py:390
msgid "Virtual machine (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
msgid "Interface (name)"
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
msgid "VM interface (name)"
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)"
msgstr "Rozhraní virtuálního počítače (ID)"
#: netbox/ipam/filtersets.py:648
#: netbox/ipam/filtersets.py:650
msgid "FHRP group (ID)"
msgstr "Skupina FHRP (ID)"
#: netbox/ipam/filtersets.py:652
#: netbox/ipam/filtersets.py:654
msgid "Is assigned to an interface"
msgstr "Je přiřazen k rozhraní"
#: netbox/ipam/filtersets.py:656
#: netbox/ipam/filtersets.py:658
msgid "Is assigned"
msgstr "Je přiřazen"
#: netbox/ipam/filtersets.py:668
#: netbox/ipam/filtersets.py:670
msgid "Service (ID)"
msgstr "Služba (ID)"
#: netbox/ipam/filtersets.py:673
#: netbox/ipam/filtersets.py:675
msgid "NAT inside IP address (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"
msgstr "Přiřazené rozhraní"
#: netbox/ipam/filtersets.py:1046
#: netbox/ipam/filtersets.py:1048
msgid "Assigned VM interface"
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)"
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"
msgstr "IP adresa"
#: netbox/ipam/filtersets.py:1167
#: netbox/ipam/filtersets.py:1169
msgid "Primary IPv4 (ID)"
msgstr "Primární IPv4 (ID)"
#: netbox/ipam/filtersets.py:1172
#: netbox/ipam/filtersets.py:1174
msgid "Primary 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/virtualization/virtualmachine/render_config.html:64
msgid "Error rendering template"
msgstr ""
msgstr "Chyba při vykreslování šablony"
#: netbox/templates/dcim/device/render_config.html:70
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
msgid "Parent Bay"
@@ -13452,7 +13453,7 @@ msgstr "Spustit znovu"
#: netbox/templates/extras/script_list.html:133
#, python-format
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
msgid "No Scripts Found"
@@ -14265,6 +14266,7 @@ msgstr "Přidat virtuální disk"
#: netbox/templates/virtualization/virtualmachine/render_config.html:70
msgid "No configuration template has been assigned for this virtual machine."
msgstr ""
"Pro tento virtuální počítač nebyla přiřazena žádná konfigurační šablona."
#: netbox/templates/vpn/ikepolicy.html:10
#: 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
msgid "Disk (MB)"
msgstr ""
msgstr "Disk (MB)"
#: netbox/virtualization/forms/bulk_edit.py:334
#: netbox/virtualization/forms/filtersets.py:251
msgid "Size (MB)"
msgstr ""
msgstr "Velikost (MB)"
#: netbox/virtualization/forms/bulk_import.py:44
msgid "Type of cluster"
@@ -15544,19 +15546,19 @@ msgstr "GREE"
#: netbox/vpn/choices.py:39
msgid "WireGuard"
msgstr ""
msgstr "WireGuard"
#: netbox/vpn/choices.py:40
msgid "OpenVPN"
msgstr ""
msgstr "OpenVPN"
#: netbox/vpn/choices.py:41
msgid "L2TP"
msgstr ""
msgstr "L2TP"
#: netbox/vpn/choices.py:42
msgid "PPTP"
msgstr ""
msgstr "PPTP"
#: netbox/vpn/choices.py:64
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
# Vladyslav V. Prodan, 2024
# Jeremy Stretch, 2024
# Michail Tatarinov, 2024
# Artem Kotik, 2025
# Michail Tatarinov, 2025
#
#, fuzzy
msgid ""
@@ -22,7 +22,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-04 05:02+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"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -6334,7 +6334,7 @@ msgstr "распределительный щит"
#: netbox/dcim/models/power.py:56
msgid "power panels"
msgstr "распределительный щиты"
msgstr "распределительные щиты"
#: netbox/dcim/models/power.py:70
#, python-brace-format
@@ -9256,7 +9256,7 @@ msgstr "SLAAC"
#: netbox/ipam/choices.py:89
msgid "Loopback"
msgstr "Обратная петля"
msgstr "Loopback"
#: netbox/ipam/choices.py:91
msgid "Anycast"
@@ -11185,7 +11185,7 @@ msgstr "Сети провайдеров"
#: netbox/netbox/navigation/menu.py:298
msgid "Power Panels"
msgstr "Распределительный щиты"
msgstr "Распределительные щиты"
#: netbox/netbox/navigation/menu.py:309
msgid "Configurations"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,6 @@
#!/bin/sh
# Create a link to this file at .git/hooks/pre-commit to
# force PEP8 validation prior to committing
#
# Ignored violations:
#
# W504: Line break after binary operator
# E501: Line too long
# TODO: Remove this file in NetBox v4.3
# This script has been maintained to ease transition to the pre-commit tool.
exec 1>&2
@@ -14,48 +9,8 @@ RED='\033[0;31m'
YELLOW='\033[0;33m'
NOCOLOR='\033[0m'
printf "${YELLOW}This script is obsolete and will be removed in a future release.\n"
printf "Please use pre-commit instead:\n"
printf "${YELLOW}The pre-commit hook script is obsolete. Please use pre-commit instead:${NOCOLOR}\n"
printf " pip install pre-commit\n"
printf " pre-commit install${NOCOLOR}\n"
if [ -d ./venv/ ]; then
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
exit 1