diff --git a/netbox_routing/api/_serializers/ospf.py b/netbox_routing/api/_serializers/ospf.py index 07d88b3..1cd4b9b 100644 --- a/netbox_routing/api/_serializers/ospf.py +++ b/netbox_routing/api/_serializers/ospf.py @@ -89,6 +89,7 @@ class Meta: 'instance', 'area', 'interface', + 'network_type', 'passive', 'priority', 'bfd', diff --git a/netbox_routing/choices/base.py b/netbox_routing/choices/base.py index fba8a53..b207f16 100644 --- a/netbox_routing/choices/base.py +++ b/netbox_routing/choices/base.py @@ -1,7 +1,7 @@ from utilities.choices import ChoiceSet -__all__ = ('AuthenticationChoices',) +__all__ = ('AuthenticationChoices', 'OSPFInterfaceTypeChoices') class AuthenticationChoices(ChoiceSet): @@ -14,3 +14,21 @@ class AuthenticationChoices(ChoiceSet): (MESSAGE_DIGEST, 'Message Digest'), (NULL, 'Null Authentication'), ] + + +class OSPFInterfaceTypeChoices(ChoiceSet): + BROADCAST = 'broadcast' + POINT_TO_POINT = 'point-to-point' + NON_BROADCAST = 'non-broadcast' + POINT_TO_MULTIPOINT = 'point-to-multipoint' + POINT_TO_MULTIPOINT_NON_BROADCAST = 'point-to-multipoint-non-broadcast' + LOOPBACK = 'loopback' + + CHOICES = [ + (BROADCAST, 'Broadcast'), + (NON_BROADCAST, 'Non-Broadcast'), + (POINT_TO_POINT, 'Point-to-Point'), + (POINT_TO_MULTIPOINT, 'Point-to-Multipoint'), + (LOOPBACK, 'Loopback'), + (POINT_TO_MULTIPOINT_NON_BROADCAST, 'Point-to-Multipoint Non-Broadcast'), + ] diff --git a/netbox_routing/filtersets/ospf.py b/netbox_routing/filtersets/ospf.py index 9e46c78..8dfd6ae 100644 --- a/netbox_routing/filtersets/ospf.py +++ b/netbox_routing/filtersets/ospf.py @@ -157,6 +157,11 @@ class OSPFInterfaceFilterSet(NetBoxModelFilterSet): to_field_name='name', label='Area', ) + network_type = django_filters.CharFilter( + field_name='network_type', + lookup_expr='iexact', + label='Network Type', + ) class Meta: model = OSPFInterface @@ -164,6 +169,7 @@ class Meta: 'instance', 'area', 'interface', + 'network_type', 'passive', 'bfd', 'priority', diff --git a/netbox_routing/forms/bulk_import/ospf.py b/netbox_routing/forms/bulk_import/ospf.py index 7a84f63..b0610f4 100644 --- a/netbox_routing/forms/bulk_import/ospf.py +++ b/netbox_routing/forms/bulk_import/ospf.py @@ -81,6 +81,12 @@ class OSPFInterfaceImportForm(NetBoxModelImportForm): to_field_name="name", help_text=_("Name of interface"), ) + network_type = CSVModelChoiceField( + queryset=OSPFInterface.objects.all(), + required=True, + to_field_name="network_type", + help_text=_("Network Type"), + ) class Meta: model = OSPFInterface @@ -89,6 +95,7 @@ class Meta: "instance", "area", "interface", + "network_type", "passive", "priority", "bfd", diff --git a/netbox_routing/forms/filtersets/ospf.py b/netbox_routing/forms/filtersets/ospf.py index 402f69d..40cebb9 100644 --- a/netbox_routing/forms/filtersets/ospf.py +++ b/netbox_routing/forms/filtersets/ospf.py @@ -10,6 +10,7 @@ from utilities.forms.fields import TagFilterField, DynamicModelMultipleChoiceField from netbox_routing.models import OSPFInstance, OSPFArea, OSPFInterface +from netbox_routing import choices __all__ = ( @@ -68,7 +69,7 @@ class OSPFInterfaceFilterForm(NetBoxModelFilterSetForm): model = OSPFInterface fieldsets = ( FieldSet('q', 'filter_id', 'tag'), - FieldSet('instance_id', 'area_id', name=_('OSPF')), + FieldSet('instance_id', 'area_id', 'network_type', name=_('OSPF')), FieldSet('device_id', 'interface_id', 'vrf_id', name=_('Device')), FieldSet('priority', 'bfd', 'authentication', name=_('Attributes')), ) @@ -78,6 +79,11 @@ class OSPFInterfaceFilterForm(NetBoxModelFilterSetForm): selector=True, label=_('Device'), ) + network_type = forms.ChoiceField( + required=False, + label=_('Network Type'), + choices=choices.OSPFInterfaceTypeChoices, + ) vrf_id = DynamicModelMultipleChoiceField( queryset=VRF.objects.all(), required=False, diff --git a/netbox_routing/forms/ospf.py b/netbox_routing/forms/ospf.py index 0034111..d765a5a 100644 --- a/netbox_routing/forms/ospf.py +++ b/netbox_routing/forms/ospf.py @@ -10,6 +10,7 @@ from netbox_routing.models import OSPFArea, OSPFInstance, OSPFInterface +from netbox_routing import choices __all__ = ( 'OSPFAreaForm', @@ -90,6 +91,11 @@ class OSPFInterfaceForm(NetBoxModelForm): 'device_id': '$device', }, ) + network_type = forms.ChoiceField( + choices=choices.OSPFInterfaceTypeChoices, + label=_('Network Type'), + required=True, + ) comments = CommentField() class Meta: @@ -99,6 +105,7 @@ class Meta: 'instance', 'area', 'interface', + 'network_type', 'passive', 'priority', 'bfd', diff --git a/netbox_routing/migrations/0017_ospfinterface_network_type.py b/netbox_routing/migrations/0017_ospfinterface_network_type.py new file mode 100644 index 0000000..4bdaaf0 --- /dev/null +++ b/netbox_routing/migrations/0017_ospfinterface_network_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.2 on 2025-10-30 12:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('netbox_routing', '0016_ospfinterface_interface_onetoonefield'), + ] + + operations = [ + migrations.AddField( + model_name='ospfinterface', + name='network_type', + field=models.CharField(default='broadcast'), + ), + ] diff --git a/netbox_routing/models/ospf.py b/netbox_routing/models/ospf.py index aa13587..2abfc2b 100644 --- a/netbox_routing/models/ospf.py +++ b/netbox_routing/models/ospf.py @@ -114,6 +114,13 @@ class OSPFInterface(PrimaryModel): blank=False, null=False, ) + network_type = models.CharField( + choices=choices.OSPFInterfaceTypeChoices, + default=choices.OSPFInterfaceTypeChoices.BROADCAST, + verbose_name='Network Type', + blank=False, + null=False, + ) passive = models.BooleanField(verbose_name='Passive', blank=True, null=True) priority = models.IntegerField(blank=True, null=True) bfd = models.BooleanField(blank=True, null=True, verbose_name='BFD') diff --git a/netbox_routing/tables/ospf.py b/netbox_routing/tables/ospf.py index 310d8db..aa3ee87 100644 --- a/netbox_routing/tables/ospf.py +++ b/netbox_routing/tables/ospf.py @@ -60,6 +60,7 @@ class Meta(NetBoxTable.Meta): 'device', 'area', 'interface', + 'network_type', 'passive', 'priority', 'bfd', diff --git a/netbox_routing/templates/netbox_routing/ospfinterface.html b/netbox_routing/templates/netbox_routing/ospfinterface.html index 2d173a4..ccda710 100644 --- a/netbox_routing/templates/netbox_routing/ospfinterface.html +++ b/netbox_routing/templates/netbox_routing/ospfinterface.html @@ -27,6 +27,11 @@
{{ object.interface|linkify }} + + Network Type + + {{ object.network_type|linkify }} +
diff --git a/netbox_routing/tests/ospf/test_api.py b/netbox_routing/tests/ospf/test_api.py index c740c10..c2f31e9 100644 --- a/netbox_routing/tests/ospf/test_api.py +++ b/netbox_routing/tests/ospf/test_api.py @@ -161,6 +161,7 @@ def setUpTestData(cls): 'instance': instance.pk, 'area': area.pk, 'interface': interfaces[3].pk, + 'network_type': 'broadcast', 'passive': True, 'priority': 2, 'authentication': 'message-digest', diff --git a/netbox_routing/tests/ospf/test_forms.py b/netbox_routing/tests/ospf/test_forms.py index 001f4d3..189814e 100644 --- a/netbox_routing/tests/ospf/test_forms.py +++ b/netbox_routing/tests/ospf/test_forms.py @@ -109,6 +109,7 @@ def test_interface_with_correct_device(self): 'interface': Interface.objects.first().pk, 'instance': OSPFInstance.objects.first().pk, 'area': OSPFArea.objects.first().pk, + 'network_type': 'broadcast', 'passive': True, } ) diff --git a/netbox_routing/tests/ospf/test_views.py b/netbox_routing/tests/ospf/test_views.py index bddb1d2..ae0c3be 100644 --- a/netbox_routing/tests/ospf/test_views.py +++ b/netbox_routing/tests/ospf/test_views.py @@ -199,6 +199,7 @@ def setUpTestData(cls): 'area': areas[3].pk, 'instance': instances[3].pk, 'passive': True, + 'network_type': 'broadcast', } cls.bulk_edit_data = {