mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-22 11:38:45 -06:00
Compare commits
4 Commits
20902-git-
...
19221-midd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3874944def | ||
|
|
ba6124f0e0 | ||
|
|
e81ccb9be6 | ||
|
|
bc83d04c8f |
43
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
Normal file
43
.github/ISSUE_TEMPLATE/03-performance.yaml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: 🏁 Performance
|
||||||
|
type: Performance
|
||||||
|
description: An opportunity to improve application performance
|
||||||
|
labels: ["netbox", "type: performance", "status: needs triage"]
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: NetBox Version
|
||||||
|
description: What version of NetBox are you currently running?
|
||||||
|
placeholder: v4.5.1
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Python Version
|
||||||
|
description: What version of Python are you currently running?
|
||||||
|
options:
|
||||||
|
- "3.12"
|
||||||
|
- "3.13"
|
||||||
|
- "3.14"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Area(s) of Concern
|
||||||
|
description: Which application interface(s) are affected?
|
||||||
|
options:
|
||||||
|
- label: User Interface
|
||||||
|
- label: REST API
|
||||||
|
- label: GraphQL API
|
||||||
|
- label: Python ORM
|
||||||
|
- label: Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Details
|
||||||
|
description: >
|
||||||
|
Describe in detail the operations being performed and the indications of a performance issue.
|
||||||
|
Include any relevant testing parameters, benchmarks, and expected results.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
@@ -43,7 +43,7 @@ IMAGEATTACHMENT_IMAGE = """
|
|||||||
<a href="{{ record.image.url }}" target="_blank" class="image-preview" data-bs-placement="top">
|
<a href="{{ record.image.url }}" target="_blank" class="image-preview" data-bs-placement="top">
|
||||||
<i class="mdi mdi-image"></i></a>
|
<i class="mdi mdi-image"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ record.get_absolute_url }}">{{ record }}</a>
|
<a href="{{ record.get_absolute_url }}">{{ record.filename|truncate_middle:16 }}</a>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NOTIFICATION_ICON = """
|
NOTIFICATION_ICON = """
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
|||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.test import tag, TestCase
|
from django.test import tag, TestCase
|
||||||
|
|
||||||
from core.models import DataSource, ObjectType
|
from core.models import AutoSyncRecord, DataSource, ObjectType
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
|
from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site, SiteGroup
|
||||||
from extras.models import ConfigContext, ConfigContextProfile, ConfigTemplate, ImageAttachment, Tag, TaggedItem
|
from extras.models import ConfigContext, ConfigContextProfile, ConfigTemplate, ImageAttachment, Tag, TaggedItem
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
@@ -754,3 +754,53 @@ class ConfigTemplateTest(TestCase):
|
|||||||
@tag('regression')
|
@tag('regression')
|
||||||
def test_config_template_with_data_source_nested_templates(self):
|
def test_config_template_with_data_source_nested_templates(self):
|
||||||
self.assertEqual(self.BASE_TEMPLATE, self.main_config_template.render({}))
|
self.assertEqual(self.BASE_TEMPLATE, self.main_config_template.render({}))
|
||||||
|
|
||||||
|
@tag('regression')
|
||||||
|
def test_autosyncrecord_cleanup_on_detach(self):
|
||||||
|
"""Test that AutoSyncRecord is deleted when detaching from DataSource."""
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
templates_dir = Path(temp_dir) / "templates"
|
||||||
|
templates_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
self._create_template_file(templates_dir, 'test.j2', 'Test content')
|
||||||
|
|
||||||
|
data_source = DataSource(
|
||||||
|
name="Test DataSource for Detach",
|
||||||
|
type="local",
|
||||||
|
source_url=str(templates_dir),
|
||||||
|
)
|
||||||
|
data_source.save()
|
||||||
|
data_source.sync()
|
||||||
|
|
||||||
|
data_file = data_source.datafiles.filter(path__endswith='test.j2').first()
|
||||||
|
|
||||||
|
# Create a ConfigTemplate with data_file and auto_sync_enabled
|
||||||
|
config_template = ConfigTemplate(
|
||||||
|
name="TestTemplateForDetach",
|
||||||
|
data_file=data_file,
|
||||||
|
auto_sync_enabled=True
|
||||||
|
)
|
||||||
|
config_template.clean()
|
||||||
|
config_template.save()
|
||||||
|
|
||||||
|
# Verify AutoSyncRecord was created
|
||||||
|
object_type = ObjectType.objects.get_for_model(ConfigTemplate)
|
||||||
|
autosync_records = AutoSyncRecord.objects.filter(
|
||||||
|
object_type=object_type,
|
||||||
|
object_id=config_template.pk
|
||||||
|
)
|
||||||
|
self.assertEqual(autosync_records.count(), 1, "AutoSyncRecord should be created")
|
||||||
|
|
||||||
|
# Detach from DataSource
|
||||||
|
config_template.data_file = None
|
||||||
|
config_template.data_source = None
|
||||||
|
config_template.auto_sync_enabled = False
|
||||||
|
config_template.clean()
|
||||||
|
config_template.save()
|
||||||
|
|
||||||
|
# Verify AutoSyncRecord was deleted
|
||||||
|
autosync_records = AutoSyncRecord.objects.filter(
|
||||||
|
object_type=object_type,
|
||||||
|
object_id=config_template.pk
|
||||||
|
)
|
||||||
|
self.assertEqual(autosync_records.count(), 0, "AutoSyncRecord should be deleted after detaching")
|
||||||
|
|||||||
@@ -569,7 +569,6 @@ class SyncedDataMixin(models.Model):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
AutoSyncRecord.objects.filter(
|
AutoSyncRecord.objects.filter(
|
||||||
datafile=self.data_file,
|
|
||||||
object_type=object_type,
|
object_type=object_type,
|
||||||
object_id=self.pk
|
object_id=self.pk
|
||||||
).delete()
|
).delete()
|
||||||
@@ -582,7 +581,6 @@ class SyncedDataMixin(models.Model):
|
|||||||
# Delete AutoSyncRecord
|
# Delete AutoSyncRecord
|
||||||
object_type = ObjectType.objects.get_for_model(self)
|
object_type = ObjectType.objects.get_for_model(self)
|
||||||
AutoSyncRecord.objects.filter(
|
AutoSyncRecord.objects.filter(
|
||||||
datafile=self.data_file,
|
|
||||||
object_type=object_type,
|
object_type=object_type,
|
||||||
object_id=self.pk
|
object_id=self.pk
|
||||||
).delete()
|
).delete()
|
||||||
|
|||||||
@@ -252,3 +252,16 @@ def isodatetime(value, spec='seconds'):
|
|||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
return mark_safe(f'<span title="{naturaltime(value)}">{text}</span>')
|
return mark_safe(f'<span title="{naturaltime(value)}">{text}</span>')
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def truncate_middle(value, length):
|
||||||
|
if len(value) <= length:
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Calculate split points for the two parts
|
||||||
|
half_len = (length - 1) // 2 # 1 for the ellipsis
|
||||||
|
first_part = value[:half_len]
|
||||||
|
second_part = value[len(value) - (length - 1 - half_len):]
|
||||||
|
|
||||||
|
return mark_safe(f"{first_part}…{second_part}")
|
||||||
|
|||||||
Reference in New Issue
Block a user