Merge branch 'netbox-community:develop' into fix_tunnel_types

This commit is contained in:
Joel McGuire 2024-12-06 10:02:43 -06:00 committed by GitHub
commit 99c9b53f2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 5812 additions and 5077 deletions

View File

@ -18,8 +18,17 @@ jobs:
NETBOX_CONFIGURATION: netbox.configuration_testing NETBOX_CONFIGURATION: netbox.configuration_testing
steps: steps:
- name: Create app token
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: 1076524
private-key: ${{ secrets.HOUSEKEEPING_SECRET_KEY }}
- name: Check out repo - name: Check out repo
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5

View File

@ -10,6 +10,15 @@ Minor releases are published in April, August, and December of each calendar yea
This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release. This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release.
#### [Version 4.1](./version-4.1.md) (September 2024)
* Circuit Groups ([#7025](https://github.com/netbox-community/netbox/issues/7025))
* VLAN Group ID Ranges ([#9627](https://github.com/netbox-community/netbox/issues/9627))
* Nested Device Modules ([#10500](https://github.com/netbox-community/netbox/issues/10500))
* Rack Types ([#12826](https://github.com/netbox-community/netbox/issues/12826))
* Plugins Catalog Integration ([#14731](https://github.com/netbox-community/netbox/issues/14731))
* User Notifications ([#15621](https://github.com/netbox-community/netbox/issues/15621))
#### [Version 4.0](./version-4.0.md) (April 2024) #### [Version 4.0](./version-4.0.md) (April 2024)
* Complete UI Refresh ([#12128](https://github.com/netbox-community/netbox/issues/12128)) * Complete UI Refresh ([#12128](https://github.com/netbox-community/netbox/issues/12128))

View File

@ -48,6 +48,7 @@ def get_device_description(device):
Name: <name> Name: <name>
Role: <role> Role: <role>
Status: <status>
Device Type: <manufacturer> <model> (<u_height>) Device Type: <manufacturer> <model> (<u_height>)
Asset tag: <asset_tag> (if defined) Asset tag: <asset_tag> (if defined)
Serial: <serial> (if defined) Serial: <serial> (if defined)
@ -55,6 +56,7 @@ def get_device_description(device):
""" """
description = f'Name: {device.name}' description = f'Name: {device.name}'
description += f'\nRole: {device.role}' description += f'\nRole: {device.role}'
description += f'\nStatus: {device.get_status_display()}'
u_height = f'{floatformat(device.device_type.u_height)}U' u_height = f'{floatformat(device.device_type.u_height)}U'
description += f'\nDevice Type: {device.device_type.manufacturer.name} {device.device_type.model} ({u_height})' description += f'\nDevice Type: {device.device_type.manufacturer.name} {device.device_type.model} ({u_height})'
if device.asset_tag: if device.asset_tag:

View File

@ -1141,12 +1141,14 @@ class ScriptView(BaseScriptView):
script_class = self._get_script_class(script) script_class = self._get_script_class(script)
if not script_class: if not script_class:
return render(request, 'extras/script.html', { return render(request, 'extras/script.html', {
'object': script,
'script': script, 'script': script,
}) })
form = script_class.as_form(initial=normalize_querydict(request.GET)) form = script_class.as_form(initial=normalize_querydict(request.GET))
return render(request, 'extras/script.html', { return render(request, 'extras/script.html', {
'object': script,
'script': script, 'script': script,
'script_class': script_class, 'script_class': script_class,
'form': form, 'form': form,
@ -1162,6 +1164,7 @@ class ScriptView(BaseScriptView):
script_class = self._get_script_class(script) script_class = self._get_script_class(script)
if not script_class: if not script_class:
return render(request, 'extras/script.html', { return render(request, 'extras/script.html', {
'object': script,
'script': script, 'script': script,
}) })
@ -1186,6 +1189,7 @@ class ScriptView(BaseScriptView):
return redirect('extras:script_result', job_pk=job.pk) return redirect('extras:script_result', job_pk=job.pk)
return render(request, 'extras/script.html', { return render(request, 'extras/script.html', {
'object': script,
'script': script, 'script': script,
'script_class': script.python_class(), 'script_class': script.python_class(),
'form': form, 'form': form,

View File

@ -76,6 +76,12 @@ class ValidatedModelSerializer(BaseModelSerializer):
Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during Extends the built-in ModelSerializer to enforce calling full_clean() on a copy of the associated instance during
validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144) validation. (DRF does not do this by default; see https://github.com/encode/django-rest-framework/issues/3144)
""" """
# Bypass DRF's built-in validation of unique constraints due to DRF bug #9410. Rely instead
# on our own custom model validation (below).
def get_unique_together_constraints(self, model):
return []
def validate(self, data): def validate(self, data):
# Skip validation if we're being used to represent a nested object # Skip validation if we're being used to represent a nested object

File diff suppressed because it is too large Load Diff

View File

@ -28,10 +28,14 @@ class DataFileLoader(BaseLoader):
raise TemplateNotFound(template) raise TemplateNotFound(template)
# Find and pre-fetch referenced templates # Find and pre-fetch referenced templates
if referenced_templates := find_referenced_templates(environment.parse(template_source)): if referenced_templates := tuple(find_referenced_templates(environment.parse(template_source))):
related_files = DataFile.objects.filter(source=self.data_source)
# None indicates the use of dynamic resolution. If dependent files are statically
# defined, we can filter by path for optimization.
if None not in referenced_templates:
related_files = related_files.filter(path__in=referenced_templates)
self.cache_templates({ self.cache_templates({
df.path: df.data_as_string for df in df.path: df.data_as_string for df in related_files
DataFile.objects.filter(source=self.data_source, path__in=referenced_templates)
}) })
return template_source, template, lambda: True return template_source, template, lambda: True