From 5cc4a76a1fd427799d6f3bd3c23d810bbb1c8c66 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 6 Apr 2022 12:32:46 -0500 Subject: [PATCH 1/7] Fixes #8546 - Restric --- .github/{ISSUE_TEMPLATRE => ISSUE_TEMPLATE}/config.yml | 0 .github/{ISSUE_TEMPLATRE => IT-Not enabled}/bug_report.yaml | 0 .github/{ISSUE_TEMPLATRE => IT-Not enabled}/feature_request.yaml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename .github/{ISSUE_TEMPLATRE => ISSUE_TEMPLATE}/config.yml (100%) rename .github/{ISSUE_TEMPLATRE => IT-Not enabled}/bug_report.yaml (100%) rename .github/{ISSUE_TEMPLATRE => IT-Not enabled}/feature_request.yaml (100%) diff --git a/.github/ISSUE_TEMPLATRE/config.yml b/.github/ISSUE_TEMPLATE/config.yml similarity index 100% rename from .github/ISSUE_TEMPLATRE/config.yml rename to .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATRE/bug_report.yaml b/.github/IT-Not enabled/bug_report.yaml similarity index 100% rename from .github/ISSUE_TEMPLATRE/bug_report.yaml rename to .github/IT-Not enabled/bug_report.yaml diff --git a/.github/ISSUE_TEMPLATRE/feature_request.yaml b/.github/IT-Not enabled/feature_request.yaml similarity index 100% rename from .github/ISSUE_TEMPLATRE/feature_request.yaml rename to .github/IT-Not enabled/feature_request.yaml From eec1a1626acdbd491aa4e48ee57e1fb0f24906d6 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 6 Apr 2022 12:35:25 -0500 Subject: [PATCH 2/7] Update Issue template --- .github/{IT-Not enabled => ISSUE_TEMPLATE}/bug_report.yaml | 0 .github/{IT-Not enabled => ISSUE_TEMPLATE}/feature_request.yaml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/{IT-Not enabled => ISSUE_TEMPLATE}/bug_report.yaml (100%) rename .github/{IT-Not enabled => ISSUE_TEMPLATE}/feature_request.yaml (100%) diff --git a/.github/IT-Not enabled/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml similarity index 100% rename from .github/IT-Not enabled/bug_report.yaml rename to .github/ISSUE_TEMPLATE/bug_report.yaml diff --git a/.github/IT-Not enabled/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml similarity index 100% rename from .github/IT-Not enabled/feature_request.yaml rename to .github/ISSUE_TEMPLATE/feature_request.yaml From a4deb55ba2e8048556035faa0430b31dcac1259b Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 6 Apr 2022 12:37:20 -0500 Subject: [PATCH 3/7] Update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1f170c5..1b8620f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ A plugin for tracking all kinds of routing information ### Current features * Static routing + +### Under development + * Dynamic routing * BGP * Templates/Group inheritance From f43819c6d83dea2044ade26e3e82f22d0a1bf7a4 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 17 May 2023 21:59:28 -0500 Subject: [PATCH 4/7] Updates for NetBox v3.5.x and advanced development for OSPF --- netbox_routing/__init__.py | 5 +- .../api/nested_serializers/objects.py | 2 +- .../api/nested_serializers/static.py | 2 +- netbox_routing/api/views/objects.py | 10 ++-- netbox_routing/api/views/static.py | 4 +- netbox_routing/models/ospf.py | 30 ++++++++++ netbox_routing/navigation.py | 59 +++++++++++-------- netbox_routing/urls.py | 1 + netbox_routing/views/__init__.py | 4 +- netbox_routing/views/static.py | 27 ++++++++- setup.py | 2 +- 11 files changed, 104 insertions(+), 42 deletions(-) create mode 100644 netbox_routing/models/ospf.py diff --git a/netbox_routing/__init__.py b/netbox_routing/__init__.py index b181896..d8f0c11 100644 --- a/netbox_routing/__init__.py +++ b/netbox_routing/__init__.py @@ -1,9 +1,6 @@ from extras.plugins import PluginConfig +from importlib.metadata import metadata -try: - from importlib.metadata import metadata -except ModuleNotFoundError: - from importlib_metadata import metadata plugin = metadata('netbox_routing') diff --git a/netbox_routing/api/nested_serializers/objects.py b/netbox_routing/api/nested_serializers/objects.py index 280dda8..3687596 100644 --- a/netbox_routing/api/nested_serializers/objects.py +++ b/netbox_routing/api/nested_serializers/objects.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from netbox.api import WritableNestedSerializer +from netbox.api.serializers import WritableNestedSerializer from netbox_routing.models import PrefixList, PrefixListEntry, RouteMap, RouteMapEntry diff --git a/netbox_routing/api/nested_serializers/static.py b/netbox_routing/api/nested_serializers/static.py index d1bb8e7..55fa1d7 100644 --- a/netbox_routing/api/nested_serializers/static.py +++ b/netbox_routing/api/nested_serializers/static.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from netbox.api import WritableNestedSerializer +from netbox.api.serializers import WritableNestedSerializer from netbox_routing.models import StaticRoute diff --git a/netbox_routing/api/views/objects.py b/netbox_routing/api/views/objects.py index 020a649..71f1e21 100644 --- a/netbox_routing/api/views/objects.py +++ b/netbox_routing/api/views/objects.py @@ -1,24 +1,24 @@ -from netbox.api.viewsets import ModelViewSet +from netbox.api.viewsets import NetBoxModelViewSet from netbox_routing.api.serializers import PrefixListSerializer, PrefixListEntrySerializer, RouteMapSerializer, \ RouteMapEntrySerializer from netbox_routing.models import PrefixList, PrefixListEntry, RouteMap, RouteMapEntry -class PrefixListViewSet(ModelViewSet): +class PrefixListViewSet(NetBoxModelViewSet): queryset = PrefixList.objects.all() serializer_class = PrefixListSerializer -class PrefixListEntryViewSet(ModelViewSet): +class PrefixListEntryViewSet(NetBoxModelViewSet): queryset = PrefixListEntry.objects.all() serializer_class = PrefixListEntrySerializer -class RouteMapViewSet(ModelViewSet): +class RouteMapViewSet(NetBoxModelViewSet): queryset = RouteMap.objects.all() serializer_class = RouteMapSerializer -class RouteMapEntryViewSet(ModelViewSet): +class RouteMapEntryViewSet(NetBoxModelViewSet): queryset = RouteMapEntry.objects.all() serializer_class = RouteMapEntrySerializer diff --git a/netbox_routing/api/views/static.py b/netbox_routing/api/views/static.py index f06f3a7..28b4133 100644 --- a/netbox_routing/api/views/static.py +++ b/netbox_routing/api/views/static.py @@ -1,8 +1,8 @@ -from netbox.api.viewsets import ModelViewSet +from netbox.api.viewsets import NetBoxModelViewSet from netbox_routing.api.serializers import StaticRouteSerializer from netbox_routing.models import StaticRoute -class StaticRouteViewSet(ModelViewSet): +class StaticRouteViewSet(NetBoxModelViewSet): queryset = StaticRoute.objects.all() serializer_class = StaticRouteSerializer diff --git a/netbox_routing/models/ospf.py b/netbox_routing/models/ospf.py new file mode 100644 index 0000000..74bcab6 --- /dev/null +++ b/netbox_routing/models/ospf.py @@ -0,0 +1,30 @@ +from django.db import models + +from netbox.models import NetBoxModel, WebhooksMixin, ChangeLoggedModel + +from netbox_routing.fields.ip import IPAddressField + + +__all__ = ( + 'OSPFInstance', + 'OSPFArea', +) + + +class OSPFInstance(NetBoxModel): + router_id = IPAddressField() + process_id = models.IntegerField() + + +class OSPFArea(NetBoxModel): + instance = models.ForeignKey( + to='netbox_routing.OSPFInstance' + ) + area_id = models.IntegerField() + interfaces = models.ManyToManyField( + to='netbox_routing.OSPFArea', + on_delete=models.PROTECT, + related_name='ospf', + blank=False, + null=False + ) diff --git a/netbox_routing/navigation.py b/netbox_routing/navigation.py index 3274ec1..7997ce3 100644 --- a/netbox_routing/navigation.py +++ b/netbox_routing/navigation.py @@ -1,29 +1,36 @@ -from extras.plugins import PluginMenuButton, PluginMenuItem +from extras.plugins import PluginMenuButton, PluginMenuItem, PluginMenu from utilities.choices import ButtonColorChoices -menu_items = ( - PluginMenuItem( - link='plugins:netbox_routing:staticroute_list', - link_text='Static Route', - buttons=( - PluginMenuButton('plugins:netbox_routing:staticroute_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN), - PluginMenuButton('plugins:netbox_routing:staticroute_import', 'Import', 'mdi mdi-upload', ButtonColorChoices.CYAN), - ) - ), - PluginMenuItem( - link='plugins:netbox_routing:prefixlist_list', - link_text='Prefix Lists', - buttons=( - PluginMenuButton('plugins:netbox_routing:prefixlist_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN), - PluginMenuButton('plugins:netbox_routing:prefixlist_import', 'Import', 'mdi mdi-upload', ButtonColorChoices.CYAN), - ) - ), - PluginMenuItem( - link='plugins:netbox_routing:routemap_list', - link_text='Route Maps', - buttons=( - PluginMenuButton('plugins:netbox_routing:routemap_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN), - PluginMenuButton('plugins:netbox_routing:routemap_import', 'Import', 'mdi mdi-upload', ButtonColorChoices.CYAN), - ) +static = PluginMenuItem( + link='plugins:netbox_routing:staticroute_list', + link_text='Static Route', + buttons=( + PluginMenuButton('plugins:netbox_routing:staticroute_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN), + PluginMenuButton('plugins:netbox_routing:staticroute_import', 'Import', 'mdi mdi-upload', ButtonColorChoices.CYAN), + ) +) +prefixlist = PluginMenuItem( + link='plugins:netbox_routing:prefixlist_list', + link_text='Prefix Lists', + buttons=( + PluginMenuButton('plugins:netbox_routing:prefixlist_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN), + PluginMenuButton('plugins:netbox_routing:prefixlist_import', 'Import', 'mdi mdi-upload', ButtonColorChoices.CYAN), + ) +) +routemap = PluginMenuItem( + link='plugins:netbox_routing:routemap_list', + link_text='Route Maps', + buttons=( + PluginMenuButton('plugins:netbox_routing:routemap_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN), + PluginMenuButton('plugins:netbox_routing:routemap_import', 'Import', 'mdi mdi-upload', ButtonColorChoices.CYAN), + ) +) + +menu = PluginMenu( + label='Netbox Routing', + groups=( + ('Routing Objects', (prefixlist, routemap )), + ('Support Contracts', (static, )), ), -) \ No newline at end of file + icon_class='mdi mdi-router' +) diff --git a/netbox_routing/urls.py b/netbox_routing/urls.py index af0bbb8..eff9442 100644 --- a/netbox_routing/urls.py +++ b/netbox_routing/urls.py @@ -11,6 +11,7 @@ path('routes/static/import/', views.StaticRouteListView.as_view(), name='staticroute_import'), path('routes/static//', views.StaticRouteView.as_view(), name='staticroute'), path('routes/static//edit/', views.StaticRouteEditView.as_view(), name='staticroute_edit'), + path('routes/static//devices/', views.StaticRouteDevicesView.as_view(), name='staticroute_devices'), path('routes/static//delete/', views.StaticRouteDeleteView.as_view(), name='staticroute_delete'), path('routes/static//changelog/', ObjectChangeLogView.as_view(), name='staticroute_changelog', kwargs={'model': StaticRoute}), diff --git a/netbox_routing/views/__init__.py b/netbox_routing/views/__init__.py index 98b372f..57f39d8 100644 --- a/netbox_routing/views/__init__.py +++ b/netbox_routing/views/__init__.py @@ -1,4 +1,5 @@ -from .static import StaticRouteListView, StaticRouteEditView, StaticRouteView, StaticRouteDeleteView +from .static import StaticRouteListView, StaticRouteDevicesView, StaticRouteEditView, StaticRouteView, \ + StaticRouteDeleteView from .objects import PrefixListView, PrefixListEditView, PrefixListListView, PrefixListDeleteView, RouteMapListView, \ RouteMapView, RouteMapEditView, RouteMapDeleteView, PrefixListEntryListView, PrefixListEntryEditView, \ @@ -8,6 +9,7 @@ __all__ = ( 'StaticRouteListView', 'StaticRouteView', + 'StaticRouteDevicesView', 'StaticRouteEditView', 'StaticRouteDeleteView', diff --git a/netbox_routing/views/static.py b/netbox_routing/views/static.py index 46df223..53b1b60 100644 --- a/netbox_routing/views/static.py +++ b/netbox_routing/views/static.py @@ -1,6 +1,7 @@ +from dcim.filtersets import DeviceFilterSet from dcim.models import Device from dcim.tables import DeviceTable -from netbox.views.generic import ObjectListView, ObjectEditView, ObjectView, ObjectDeleteView +from netbox.views.generic import ObjectListView, ObjectEditView, ObjectView, ObjectDeleteView, ObjectChildrenView from netbox_routing.filtersets.static import StaticRouteFilterSet from netbox_routing.forms import StaticRouteForm from netbox_routing.forms.filtersets.static import StaticRouteFilterSetForm @@ -11,11 +12,15 @@ __all__ = ( 'StaticRouteListView', 'StaticRouteView', + 'StaticRouteDevicesView', 'StaticRouteEditView', 'StaticRouteDeleteView', ) +from utilities.views import register_model_view, ViewTab + +@register_model_view(StaticRoute, name='list') class StaticRouteListView(ObjectListView): queryset = StaticRoute.objects.all() table = StaticRouteTable @@ -23,6 +28,7 @@ class StaticRouteListView(ObjectListView): filterset_form = StaticRouteFilterSetForm +@register_model_view(StaticRoute) class StaticRouteView(ObjectView): queryset = StaticRoute.objects.all() template_name = 'netbox_routing/staticroute.html' @@ -43,10 +49,29 @@ def get_extra_context(self, request, instance): } +@register_model_view(StaticRoute, name='assignments') +class StaticRouteDevicesView(ObjectChildrenView): + # template_name = 'dcim//.html' + queryset = StaticRoute.objects.all() + child_model = Device + table = DeviceTable + filterset = DeviceFilterSet + actions = [] + tab = ViewTab( + label='Assigned Devices', + badge=lambda obj: Device.objects.filter(static_routes=obj).count(), + ) + + def get_children(self, request, parent): + return self.child_model.objects.filter(static_routes=parent) + + +@register_model_view(StaticRoute, name='edit') class StaticRouteEditView(ObjectEditView): queryset = StaticRoute.objects.all() form = StaticRouteForm +@register_model_view(StaticRoute, name='delete') class StaticRouteDeleteView(ObjectDeleteView): pass diff --git a/setup.py b/setup.py index f64b29a..762c08c 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ 'Programming Language :: Python :: 3', ], install_requires=[ - 'importlib', + 'django-polymorphic', ], packages=find_packages(), include_package_data=True, From 75c9cc9f8686ebf76efa5076e286e97acb971967 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 17 May 2023 22:05:49 -0500 Subject: [PATCH 5/7] v1.0.0-beta3 --- netbox_routing/migrations/0001_initial.py | 106 ++++++++++++++++++++++ netbox_routing/migrations/__init__.py | 0 2 files changed, 106 insertions(+) create mode 100644 netbox_routing/migrations/0001_initial.py create mode 100644 netbox_routing/migrations/__init__.py diff --git a/netbox_routing/migrations/0001_initial.py b/netbox_routing/migrations/0001_initial.py new file mode 100644 index 0000000..f16d9f3 --- /dev/null +++ b/netbox_routing/migrations/0001_initial.py @@ -0,0 +1,106 @@ +# Generated by Django 4.0.3 on 2022-04-01 18:02 + +import django.core.serializers.json +from django.db import migrations, models +import django.db.models.deletion +import ipam.fields +import netbox_routing.fields.ip +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('ipam', '0057_created_datetimefield'), + ('extras', '0072_created_datetimefield'), + ('dcim', '0153_created_datetimefield'), + ] + + operations = [ + migrations.CreateModel( + name='PrefixList', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), + ('name', models.CharField(max_length=255)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='RouteMap', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), + ('name', models.CharField(max_length=255)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='StaticRoute', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), + ('prefix', ipam.fields.IPNetworkField()), + ('next_hop', netbox_routing.fields.ip.IPAddressField()), + ('name', models.CharField(blank=True, max_length=50, null=True)), + ('metric', models.PositiveSmallIntegerField()), + ('permanent', models.BooleanField()), + ('devices', models.ManyToManyField(related_name='static_routes', to='dcim.device')), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ('vrf', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='staticroutes', to='ipam.vrf')), + ], + ), + migrations.CreateModel( + name='RouteMapEntry', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), + ('type', models.CharField(max_length=6)), + ('sequence', models.PositiveSmallIntegerField()), + ('route_map', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='entries', to='netbox_routing.routemap')), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='PrefixListEntry', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)), + ('sequence', models.PositiveSmallIntegerField()), + ('type', models.CharField(max_length=6)), + ('prefix', ipam.fields.IPNetworkField()), + ('ge', models.PositiveSmallIntegerField()), + ('le', models.PositiveSmallIntegerField()), + ('prefix_list', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='entries', to='netbox_routing.prefixlist')), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddConstraint( + model_name='staticroute', + constraint=models.CheckConstraint(check=models.Q(models.Q(('metric__lte', 255), ('metric__gte', 0))), name='metric_gte_lte'), + ), + ] diff --git a/netbox_routing/migrations/__init__.py b/netbox_routing/migrations/__init__.py new file mode 100644 index 0000000..e69de29 From 6add3e81f71191e515dfa6f4632608184efabc85 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 17 May 2023 22:07:56 -0500 Subject: [PATCH 6/7] Update gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b6e4761..e4e0ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,5 @@ dmypy.json # Pyre type checker .pyre/ + +.idea \ No newline at end of file From c6d02e43930edb8eca843d98f523952250793142 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Wed, 17 May 2023 22:08:47 -0500 Subject: [PATCH 7/7] Version Bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 762c08c..222d77f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='netbox-routing', - version='0.0.1', + version='0.0.2', description='NetBox Routing', long_description='Plugin for documentation of routing configuration and objects', url='https://github.com/dansheps/netbox-routing/',