mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-30 13:48:16 +01:00
Compare commits
1 Commits
main
...
21303-cach
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b8caef5f |
@@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from jsonschema.exceptions import ValidationError as JSONValidationError
|
||||
|
||||
from dcim.choices import *
|
||||
from dcim.utils import create_port_mappings, update_interface_bridges
|
||||
from dcim.utils import update_interface_bridges
|
||||
from extras.models import ConfigContextModel, CustomField
|
||||
from netbox.models import PrimaryModel
|
||||
from netbox.models.features import ImageAttachmentsMixin
|
||||
@@ -361,7 +361,5 @@ class Module(TrackingModelMixin, PrimaryModel, ConfigContextModel):
|
||||
update_fields=update_fields
|
||||
)
|
||||
|
||||
# Replicate any front/rear port mappings from the ModuleType
|
||||
create_port_mappings(self.device, self.module_type, self)
|
||||
# Interface bridges have to be set after interface instantiation
|
||||
update_interface_bridges(self.device, self.module_type.interfacetemplates, self)
|
||||
|
||||
@@ -875,142 +875,6 @@ class ModuleBayTestCase(TestCase):
|
||||
self.assertIsNone(bay2.parent)
|
||||
self.assertIsNone(bay2.module)
|
||||
|
||||
def test_module_installation_creates_port_mappings(self):
|
||||
"""
|
||||
Test that installing a module with front/rear port templates correctly
|
||||
creates PortMapping instances for the device.
|
||||
"""
|
||||
device = Device.objects.first()
|
||||
manufacturer = Manufacturer.objects.first()
|
||||
module_bay = ModuleBay.objects.create(device=device, name='Test Bay PortMapping 1')
|
||||
|
||||
# Create a module type with a rear port template
|
||||
module_type_with_mappings = ModuleType.objects.create(
|
||||
manufacturer=manufacturer,
|
||||
model='Module Type With Mappings',
|
||||
)
|
||||
|
||||
# Create a rear port template with 12 positions (splice)
|
||||
rear_port_template = RearPortTemplate.objects.create(
|
||||
module_type=module_type_with_mappings,
|
||||
name='Rear Port 1',
|
||||
type=PortTypeChoices.TYPE_SPLICE,
|
||||
positions=12,
|
||||
)
|
||||
|
||||
# Create 12 front port templates mapped to the rear port
|
||||
front_port_templates = []
|
||||
for i in range(1, 13):
|
||||
front_port_template = FrontPortTemplate.objects.create(
|
||||
module_type=module_type_with_mappings,
|
||||
name=f'port {i}',
|
||||
type=PortTypeChoices.TYPE_LC,
|
||||
positions=1,
|
||||
)
|
||||
front_port_templates.append(front_port_template)
|
||||
|
||||
# Create port template mapping
|
||||
PortTemplateMapping.objects.create(
|
||||
device_type=None,
|
||||
module_type=module_type_with_mappings,
|
||||
front_port=front_port_template,
|
||||
front_port_position=1,
|
||||
rear_port=rear_port_template,
|
||||
rear_port_position=i,
|
||||
)
|
||||
|
||||
# Install the module
|
||||
module = Module.objects.create(
|
||||
device=device,
|
||||
module_bay=module_bay,
|
||||
module_type=module_type_with_mappings,
|
||||
status=ModuleStatusChoices.STATUS_ACTIVE,
|
||||
)
|
||||
|
||||
# Verify that front ports were created
|
||||
front_ports = FrontPort.objects.filter(device=device, module=module)
|
||||
self.assertEqual(front_ports.count(), 12)
|
||||
|
||||
# Verify that the rear port was created
|
||||
rear_ports = RearPort.objects.filter(device=device, module=module)
|
||||
self.assertEqual(rear_ports.count(), 1)
|
||||
rear_port = rear_ports.first()
|
||||
self.assertEqual(rear_port.positions, 12)
|
||||
|
||||
# Verify that port mappings were created
|
||||
port_mappings = PortMapping.objects.filter(front_port__module=module)
|
||||
self.assertEqual(port_mappings.count(), 12)
|
||||
|
||||
# Verify each mapping is correct
|
||||
for i, front_port_template in enumerate(front_port_templates, start=1):
|
||||
front_port = FrontPort.objects.get(
|
||||
device=device,
|
||||
name=front_port_template.name,
|
||||
module=module,
|
||||
)
|
||||
|
||||
# Check that a mapping exists for this front port
|
||||
mapping = PortMapping.objects.get(
|
||||
device=device,
|
||||
front_port=front_port,
|
||||
front_port_position=1,
|
||||
)
|
||||
|
||||
self.assertEqual(mapping.rear_port, rear_port)
|
||||
self.assertEqual(mapping.front_port_position, 1)
|
||||
self.assertEqual(mapping.rear_port_position, i)
|
||||
|
||||
def test_module_installation_without_mappings(self):
|
||||
"""
|
||||
Test that installing a module without port template mappings
|
||||
doesn't create any PortMapping instances.
|
||||
"""
|
||||
device = Device.objects.first()
|
||||
manufacturer = Manufacturer.objects.first()
|
||||
module_bay = ModuleBay.objects.create(device=device, name='Test Bay PortMapping 2')
|
||||
|
||||
# Create a module type without any port template mappings
|
||||
module_type_no_mappings = ModuleType.objects.create(
|
||||
manufacturer=manufacturer,
|
||||
model='Module Type Without Mappings',
|
||||
)
|
||||
|
||||
# Create a rear port template
|
||||
RearPortTemplate.objects.create(
|
||||
module_type=module_type_no_mappings,
|
||||
name='Rear Port 1',
|
||||
type=PortTypeChoices.TYPE_SPLICE,
|
||||
positions=12,
|
||||
)
|
||||
|
||||
# Create front port templates but DO NOT create PortTemplateMapping rows
|
||||
for i in range(1, 13):
|
||||
FrontPortTemplate.objects.create(
|
||||
module_type=module_type_no_mappings,
|
||||
name=f'port {i}',
|
||||
type=PortTypeChoices.TYPE_LC,
|
||||
positions=1,
|
||||
)
|
||||
|
||||
# Install the module
|
||||
module = Module.objects.create(
|
||||
device=device,
|
||||
module_bay=module_bay,
|
||||
module_type=module_type_no_mappings,
|
||||
status=ModuleStatusChoices.STATUS_ACTIVE,
|
||||
)
|
||||
|
||||
# Verify no port mappings were created for this module
|
||||
port_mappings = PortMapping.objects.filter(
|
||||
device=device,
|
||||
front_port__module=module,
|
||||
front_port_position=1,
|
||||
)
|
||||
self.assertEqual(port_mappings.count(), 0)
|
||||
self.assertEqual(FrontPort.objects.filter(module=module).count(), 12)
|
||||
self.assertEqual(RearPort.objects.filter(module=module).count(), 1)
|
||||
self.assertEqual(PortMapping.objects.filter(front_port__module=module).count(), 0)
|
||||
|
||||
|
||||
class CableTestCase(TestCase):
|
||||
|
||||
|
||||
@@ -85,13 +85,13 @@ def update_interface_bridges(device, interface_templates, module=None):
|
||||
interface.save()
|
||||
|
||||
|
||||
def create_port_mappings(device, device_or_module_type, module=None):
|
||||
def create_port_mappings(device, device_type, module=None):
|
||||
"""
|
||||
Replicate all front/rear port mappings from a DeviceType or ModuleType to the given device.
|
||||
Replicate all front/rear port mappings from a DeviceType to the given device.
|
||||
"""
|
||||
from dcim.models import FrontPort, PortMapping, RearPort
|
||||
|
||||
templates = device_or_module_type.port_mappings.prefetch_related('front_port', 'rear_port')
|
||||
templates = device_type.port_mappings.prefetch_related('front_port', 'rear_port')
|
||||
|
||||
# Cache front & rear ports for efficient lookups by name
|
||||
front_ports = {
|
||||
|
||||
@@ -53,9 +53,9 @@ def serialize_for_event(instance):
|
||||
def get_snapshots(instance, event_type):
|
||||
snapshots = {
|
||||
'prechange': getattr(instance, '_prechange_snapshot', None),
|
||||
'postchange': None,
|
||||
'postchange': getattr(instance, '_postchange_snapshot', None),
|
||||
}
|
||||
if event_type != OBJECT_DELETED:
|
||||
if snapshots['postchange'] is None and event_type != OBJECT_DELETED:
|
||||
# Use model's serialize_object() method if defined; fall back to serialize_object() utility function
|
||||
if hasattr(instance, 'serialize_object'):
|
||||
snapshots['postchange'] = instance.serialize_object()
|
||||
|
||||
@@ -121,7 +121,8 @@ class ChangeLoggingMixin(DeleteMixin, models.Model):
|
||||
if hasattr(self, '_prechange_snapshot'):
|
||||
objectchange.prechange_data = self._prechange_snapshot
|
||||
if action in (ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE):
|
||||
objectchange.postchange_data = self.serialize_object(exclude=exclude)
|
||||
self._postchange_snapshot = self.serialize_object(exclude=exclude)
|
||||
objectchange.postchange_data = self._postchange_snapshot
|
||||
|
||||
return objectchange
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-30 05:18+0000\n"
|
||||
"POT-Creation-Date: 2026-01-29 05:16+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -293,8 +293,8 @@ msgstr ""
|
||||
|
||||
#: netbox/circuits/filtersets.py:278 netbox/circuits/filtersets.py:382
|
||||
#: netbox/circuits/filtersets.py:547 netbox/core/filtersets.py:85
|
||||
#: netbox/core/filtersets.py:154 netbox/core/filtersets.py:180
|
||||
#: netbox/core/filtersets.py:220 netbox/dcim/filtersets.py:810
|
||||
#: netbox/core/filtersets.py:150 netbox/core/filtersets.py:176
|
||||
#: netbox/core/filtersets.py:216 netbox/dcim/filtersets.py:810
|
||||
#: netbox/dcim/filtersets.py:1568 netbox/dcim/filtersets.py:2692
|
||||
#: netbox/extras/filtersets.py:48 netbox/extras/filtersets.py:71
|
||||
#: netbox/extras/filtersets.py:101 netbox/extras/filtersets.py:142
|
||||
@@ -763,7 +763,7 @@ msgstr ""
|
||||
#: netbox/circuits/forms/filtersets.py:132
|
||||
#: netbox/circuits/forms/filtersets.py:322
|
||||
#: netbox/circuits/forms/filtersets.py:338 netbox/core/forms/filtersets.py:75
|
||||
#: netbox/core/forms/filtersets.py:147 netbox/dcim/forms/bulk_edit.py:818
|
||||
#: netbox/core/forms/filtersets.py:143 netbox/dcim/forms/bulk_edit.py:818
|
||||
#: netbox/dcim/forms/bulk_import.py:480 netbox/dcim/forms/filtersets.py:199
|
||||
#: netbox/dcim/forms/filtersets.py:232 netbox/dcim/forms/filtersets.py:1012
|
||||
#: netbox/dcim/forms/filtersets.py:1155 netbox/dcim/forms/filtersets.py:1285
|
||||
@@ -2189,7 +2189,7 @@ msgstr ""
|
||||
msgid "Data source (name)"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/filtersets.py:190 netbox/dcim/filtersets.py:521
|
||||
#: netbox/core/filtersets.py:186 netbox/dcim/filtersets.py:521
|
||||
#: netbox/extras/filtersets.py:302 netbox/extras/filtersets.py:355
|
||||
#: netbox/extras/filtersets.py:401 netbox/extras/filtersets.py:424
|
||||
#: netbox/extras/filtersets.py:490 netbox/users/filtersets.py:31
|
||||
@@ -2197,7 +2197,7 @@ msgstr ""
|
||||
msgid "User (ID)"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/filtersets.py:196
|
||||
#: netbox/core/filtersets.py:192
|
||||
msgid "User name"
|
||||
msgstr ""
|
||||
|
||||
@@ -2277,7 +2277,7 @@ msgstr ""
|
||||
msgid "Creation"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:82 netbox/core/forms/filtersets.py:172
|
||||
#: netbox/core/forms/filtersets.py:82 netbox/core/forms/filtersets.py:168
|
||||
#: netbox/extras/forms/filtersets.py:577 netbox/extras/tables/tables.py:271
|
||||
#: netbox/extras/tables/tables.py:338 netbox/extras/tables/tables.py:364
|
||||
#: netbox/extras/tables/tables.py:383 netbox/extras/tables/tables.py:415
|
||||
@@ -2288,44 +2288,39 @@ msgstr ""
|
||||
msgid "Object Type"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:92 netbox/core/tables/jobs.py:46
|
||||
#: netbox/templates/core/job.html:63 netbox/templates/core/rq_task.html:61
|
||||
msgid "Queue"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:96
|
||||
#: netbox/core/forms/filtersets.py:92
|
||||
msgid "Created after"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:101
|
||||
#: netbox/core/forms/filtersets.py:97
|
||||
msgid "Created before"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:106
|
||||
#: netbox/core/forms/filtersets.py:102
|
||||
msgid "Scheduled after"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:111
|
||||
#: netbox/core/forms/filtersets.py:107
|
||||
msgid "Scheduled before"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:116
|
||||
#: netbox/core/forms/filtersets.py:112
|
||||
msgid "Started after"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:121
|
||||
#: netbox/core/forms/filtersets.py:117
|
||||
msgid "Started before"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:126
|
||||
#: netbox/core/forms/filtersets.py:122
|
||||
msgid "Completed after"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:131
|
||||
#: netbox/core/forms/filtersets.py:127
|
||||
msgid "Completed before"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:138 netbox/core/forms/filtersets.py:167
|
||||
#: netbox/core/forms/filtersets.py:134 netbox/core/forms/filtersets.py:163
|
||||
#: netbox/dcim/forms/bulk_edit.py:455 netbox/dcim/forms/filtersets.py:509
|
||||
#: netbox/dcim/forms/model_forms.py:326 netbox/extras/forms/filtersets.py:572
|
||||
#: netbox/extras/forms/filtersets.py:592 netbox/extras/tables/tables.py:391
|
||||
@@ -2341,22 +2336,22 @@ msgstr ""
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:146 netbox/core/tables/change_logging.py:15
|
||||
#: netbox/core/tables/jobs.py:72 netbox/extras/tables/tables.py:773
|
||||
#: netbox/core/forms/filtersets.py:142 netbox/core/tables/change_logging.py:15
|
||||
#: netbox/core/tables/jobs.py:69 netbox/extras/tables/tables.py:773
|
||||
#: netbox/extras/tables/tables.py:828
|
||||
#: netbox/templates/core/objectchange.html:32
|
||||
msgid "Time"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:151 netbox/extras/forms/filtersets.py:561
|
||||
#: netbox/core/forms/filtersets.py:147 netbox/extras/forms/filtersets.py:561
|
||||
msgid "After"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:156 netbox/extras/forms/filtersets.py:566
|
||||
#: netbox/core/forms/filtersets.py:152 netbox/extras/forms/filtersets.py:566
|
||||
msgid "Before"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/forms/filtersets.py:160 netbox/core/tables/change_logging.py:29
|
||||
#: netbox/core/forms/filtersets.py:156 netbox/core/tables/change_logging.py:29
|
||||
#: netbox/extras/forms/model_forms.py:484
|
||||
#: netbox/templates/core/objectchange.html:46
|
||||
#: netbox/templates/extras/eventrule.html:71
|
||||
@@ -2726,36 +2721,28 @@ msgid "job ID"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:116
|
||||
msgid "queue name"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:119
|
||||
msgid "Name of the queue in which this job was enqueued"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:122
|
||||
msgid "log entries"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:138
|
||||
#: netbox/core/models/jobs.py:132
|
||||
msgid "job"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:139
|
||||
#: netbox/core/models/jobs.py:133
|
||||
msgid "jobs"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:169
|
||||
#: netbox/core/models/jobs.py:163
|
||||
#, python-brace-format
|
||||
msgid "Jobs cannot be assigned to this object type ({type})."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:226
|
||||
#: netbox/core/models/jobs.py:216
|
||||
#, python-brace-format
|
||||
msgid "Invalid status for job termination. Choices are: {choices}"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/models/jobs.py:283
|
||||
#: netbox/core/models/jobs.py:273
|
||||
msgid ""
|
||||
"enqueue() cannot be called with values for both schedule_at and immediate."
|
||||
msgstr ""
|
||||
@@ -2801,7 +2788,7 @@ msgstr ""
|
||||
msgid "Request ID"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/tables/change_logging.py:45 netbox/core/tables/jobs.py:79
|
||||
#: netbox/core/tables/change_logging.py:45 netbox/core/tables/jobs.py:76
|
||||
#: netbox/extras/tables/tables.py:784 netbox/extras/tables/tables.py:841
|
||||
#: netbox/templates/core/objectchange.html:68
|
||||
msgid "Message"
|
||||
@@ -2844,16 +2831,16 @@ msgstr ""
|
||||
msgid "Interval"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/tables/jobs.py:49
|
||||
#: netbox/core/tables/jobs.py:46
|
||||
msgid "Log Entries"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/tables/jobs.py:76 netbox/extras/tables/tables.py:778
|
||||
#: netbox/core/tables/jobs.py:73 netbox/extras/tables/tables.py:778
|
||||
#: netbox/extras/tables/tables.py:832
|
||||
msgid "Level"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/core/tables/jobs.py:83
|
||||
#: netbox/core/tables/jobs.py:80
|
||||
msgid "No log entries"
|
||||
msgstr ""
|
||||
|
||||
@@ -8886,7 +8873,7 @@ msgstr ""
|
||||
|
||||
#: netbox/extras/forms/filtersets.py:176 netbox/extras/forms/filtersets.py:377
|
||||
#: netbox/extras/forms/filtersets.py:400 netbox/extras/forms/filtersets.py:496
|
||||
#: netbox/extras/forms/model_forms.py:690 netbox/templates/core/job.html:73
|
||||
#: netbox/extras/forms/model_forms.py:690 netbox/templates/core/job.html:69
|
||||
#: netbox/templates/extras/eventrule.html:84
|
||||
msgid "Data"
|
||||
msgstr ""
|
||||
@@ -13587,6 +13574,10 @@ msgstr ""
|
||||
msgid "Enqueue"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/core/rq_task.html:61
|
||||
msgid "Queue"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/templates/core/rq_task.html:65
|
||||
msgid "Timeout"
|
||||
msgstr ""
|
||||
|
||||
Reference in New Issue
Block a user