diff --git a/netbox/dcim/api/serializers_/device_components.py b/netbox/dcim/api/serializers_/device_components.py index b26cf9bbb..ecd8207e3 100644 --- a/netbox/dcim/api/serializers_/device_components.py +++ b/netbox/dcim/api/serializers_/device_components.py @@ -338,9 +338,9 @@ class FrontPortSerializer(NetBoxModelSerializer, CabledObjectSerializer): class Meta: model = FrontPort fields = [ - 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', - 'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', - 'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied', + 'id', 'url', 'display_url', 'display', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', + 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers', 'link_peers_type', 'tags', + 'custom_fields', 'created', 'last_updated', '_occupied', ] brief_fields = ('id', 'url', 'display', 'device', 'name', 'description', 'cable', '_occupied') diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 9c161aa54..52ee68929 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -2101,14 +2101,15 @@ class FrontPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet) choices=PortTypeChoices, null_value=None ) - rear_port_id = django_filters.ModelMultipleChoiceFilter( - queryset=RearPort.objects.all() - ) + # TODO + # rear_port_id = django_filters.ModelMultipleChoiceFilter( + # queryset=RearPort.objects.all() + # ) class Meta: model = FrontPort fields = ( - 'id', 'name', 'label', 'type', 'color', 'rear_port_position', 'description', 'mark_connected', 'cable_end', + 'id', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable_end', 'cable_position', ) @@ -2118,6 +2119,10 @@ class RearPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet): choices=PortTypeChoices, null_value=None ) + # TODO + # front_port_id = django_filters.ModelMultipleChoiceFilter( + # queryset=FrontPort.objects.all() + # ) class Meta: model = RearPort diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index ba0b44b0d..eb41af0eb 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -1090,8 +1090,7 @@ class FrontPortImportForm(OwnerCSVMixin, NetBoxModelImportForm): class Meta: model = FrontPort fields = ( - 'device', 'name', 'label', 'type', 'color', 'mark_connected', 'rear_port', 'rear_port_position', - 'description', 'owner', 'tags' + 'device', 'name', 'label', 'type', 'color', 'mark_connected', 'positions', 'description', 'owner', 'tags' ) def __init__(self, *args, **kwargs): diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 75a827476..af0662d1e 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1595,8 +1595,8 @@ class FrontPortForm(ModularDeviceComponentForm): class Meta: model = FrontPort fields = [ - 'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'mark_connected', - 'description', 'owner', 'tags', + 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'owner', + 'tags', ] diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index d9afabddf..aa14ed9cc 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -284,7 +284,7 @@ class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm): ) class Meta(model_forms.FrontPortForm.Meta): - exclude = ('name', 'label', 'rear_port', 'rear_port_position') + exclude = ('name', 'label', 'positions') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 111902dd9..398c065a6 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -395,11 +395,7 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig class FrontPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() - rear_port: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - rear_port_id: ID | None = strawberry_django.filter_field() - rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + rear_ports: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/dcim/migrations/0222_frontport_positions.py b/netbox/dcim/migrations/0222_frontport_positions.py new file mode 100644 index 000000000..2ef7ff088 --- /dev/null +++ b/netbox/dcim/migrations/0222_frontport_positions.py @@ -0,0 +1,31 @@ +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0221_m2m_port_assignments'), + ] + + operations = [ + migrations.RemoveField( + model_name='frontport', + name='rear_port', + ), + migrations.RemoveField( + model_name='frontport', + name='rear_port_position', + ), + migrations.AddField( + model_name='frontport', + name='positions', + field=models.PositiveSmallIntegerField( + default=1, + validators=[ + django.core.validators.MinValueValidator(1), + django.core.validators.MaxValueValidator(1024) + ] + ), + ), + ] diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 3f90963a7..f906add7c 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -1120,26 +1120,18 @@ class FrontPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin): verbose_name=_('color'), blank=True ) - rear_ports = models.ManyToManyField( - to='dcim.RearPort', - through='dcim.PortAssignment', - related_name='front_ports', - ) - - # Legacy fields - rear_port = models.ForeignKey( - to='dcim.RearPort', - on_delete=models.CASCADE, - related_name='frontports' - ) - rear_port_position = models.PositiveSmallIntegerField( - verbose_name=_('rear port position'), + positions = models.PositiveSmallIntegerField( + verbose_name=_('positions'), default=1, validators=[ MinValueValidator(PORT_POSITION_MIN), MaxValueValidator(PORT_POSITION_MAX) ], - help_text=_('Mapped position on corresponding rear port') + ) + rear_ports = models.ManyToManyField( + to='dcim.RearPort', + through='dcim.PortAssignment', + related_name='front_ports', ) clone_fields = ('device', 'type', 'color') @@ -1205,7 +1197,6 @@ class RearPort(ModularComponentModel, CabledObjectModel, TrackingModelMixin): MinValueValidator(PORT_POSITION_MIN), MaxValueValidator(PORT_POSITION_MAX) ], - help_text=_('Number of front ports which may be mapped') ) clone_fields = ('device', 'type', 'color', 'positions') diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index eb1825c1a..636d7f484 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -156,9 +156,9 @@ def extend_rearport_cable_paths(instance, created, raw, **kwargs): When a new FrontPort is created, add it to any CablePaths which end at its corresponding RearPort. """ if created and not raw: - rearport = instance.rear_port - for cablepath in CablePath.objects.filter(_nodes__contains=rearport): - cablepath.retrace() + for rear_port in instance.rear_ports.all(): + for cablepath in CablePath.objects.filter(_nodes__contains=rear_port): + cablepath.retrace() @receiver(post_save, sender=Interface) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 5f9467297..cfdeb14ce 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -763,12 +763,12 @@ class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable): class Meta(DeviceComponentTable.Meta): model = models.FrontPort fields = ( - 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'rear_port', - 'rear_port_position', 'description', 'mark_connected', 'cable', 'cable_color', 'link_peer', + 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'type', 'color', 'positions', 'description', + 'mark_connected', 'cable', 'cable_color', 'link_peer', 'inventory_items', 'tags', 'created', 'last_updated', ) default_columns = ( - 'pk', 'name', 'device', 'label', 'type', 'color', 'rear_port', 'rear_port_position', 'description', + 'pk', 'name', 'device', 'label', 'type', 'color', 'positions', 'description', ) diff --git a/netbox/templates/dcim/frontport.html b/netbox/templates/dcim/frontport.html index 9f4d23e60..dc68fbfa9 100644 --- a/netbox/templates/dcim/frontport.html +++ b/netbox/templates/dcim/frontport.html @@ -47,12 +47,8 @@ - {% trans "Rear Port" %} - {{ object.rear_port|linkify }} - - - {% trans "Rear Port Position" %} - {{ object.rear_port_position }} + {% trans "Positions" %} + {{ object.positions }} {% trans "Description" %}