diff --git a/netbox_routing/api/_serializers/eigrp.py b/netbox_routing/api/_serializers/eigrp.py
new file mode 100644
index 0000000..ef23a82
--- /dev/null
+++ b/netbox_routing/api/_serializers/eigrp.py
@@ -0,0 +1,71 @@
+from rest_framework import serializers
+
+from dcim.api.serializers_.device_components import InterfaceSerializer
+from dcim.api.serializers_.devices import DeviceSerializer
+from ipam.api.serializers_.ip import PrefixSerializer
+from netbox.api.serializers import NetBoxModelSerializer
+from netbox_routing.models import (
+ EIGRPRouter, EIGRPAddressFamily, EIGRPNetwork, EIGRPInterface
+)
+
+
+__all__ = (
+ 'EIGRPRouterSerializer',
+ 'EIGRPAddressFamilySerializer',
+ 'EIGRPNetworkSerializer',
+ 'EIGRPInterfaceSerializer',
+)
+
+
+class EIGRPRouterSerializer(NetBoxModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_routing-api:eigrprouter-detail')
+ device = DeviceSerializer(nested=True)
+
+ class Meta:
+ model = EIGRPRouter
+ fields = ('url', 'id', 'display', 'name', 'pid', 'rid', 'device', 'description', 'comments', )
+ brief_fields = ('url', 'id', 'display', 'name', 'pid', 'rid', 'device', )
+
+
+class EIGRPAddressFamilySerializer(NetBoxModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_routing-api:eigrpaddressfamily-detail')
+ router = EIGRPRouterSerializer(nested=True)
+
+
+ class Meta:
+ model = EIGRPAddressFamily
+ fields = (
+ 'url', 'id', 'display', 'router', 'family', 'description', 'comments',
+ )
+ brief_fields = ('url', 'id', 'display', 'router', 'family',)
+
+
+class EIGRPNetworkSerializer(NetBoxModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_routing-api:eigrpnetwork-detail')
+ router = EIGRPRouterSerializer(nested=True)
+ address_family = EIGRPAddressFamilySerializer(nested=True)
+ network = PrefixSerializer(nested=True)
+
+ class Meta:
+ model = EIGRPNetwork
+ fields = (
+ 'url', 'id', 'display', 'router', 'address_family', 'network', 'description', 'comments',
+ )
+ brief_fields = ('url', 'id', 'display', 'router', 'address_family', 'network',)
+
+
+class EIGRPInterfaceSerializer(NetBoxModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_routing-api:eigrpinterface-detail')
+ router = EIGRPRouterSerializer(nested=True)
+ address_family = EIGRPAddressFamilySerializer(nested=True)
+ interface = InterfaceSerializer(nested=True)
+
+ class Meta:
+ model = EIGRPInterface
+ fields = (
+ 'url', 'id', 'display', 'router', 'address_family', 'interface', 'passive', 'bfd',
+ 'authentication', 'passphrase', 'description', 'comments',
+ )
+ brief_fields = (
+ 'url', 'id', 'display', 'router', 'address_family', 'interface',
+ )
diff --git a/netbox_routing/api/serializers.py b/netbox_routing/api/serializers.py
index 03a2a88..579285f 100644
--- a/netbox_routing/api/serializers.py
+++ b/netbox_routing/api/serializers.py
@@ -6,6 +6,7 @@
BGPRouterSerializer, BGPScopeSerializer, BGPAddressFamilySerializer, BGPSettingSerializer
)
from netbox_routing.api._serializers.ospf import *
+from netbox_routing.api._serializers.eigrp import *
__all__ = (
'StaticRouteSerializer',
@@ -14,6 +15,11 @@
'OSPFAreaSerializer',
'OSPFInterfaceSerializer',
+ 'EIGRPRouterSerializer',
+ 'EIGRPAddressFamilySerializer',
+ 'EIGRPNetworkSerializer',
+ 'EIGRPInterfaceSerializer',
+
'PrefixListSerializer',
'PrefixListEntrySerializer',
'RouteMapSerializer',
diff --git a/netbox_routing/api/urls.py b/netbox_routing/api/urls.py
index ed6f77c..00d6761 100644
--- a/netbox_routing/api/urls.py
+++ b/netbox_routing/api/urls.py
@@ -1,7 +1,8 @@
from netbox.api.routers import NetBoxRouter
from .views import StaticRouteViewSet, PrefixListViewSet, RouteMapViewSet, PrefixListEntryViewSet, \
RouteMapEntryViewSet, OSPFInstanceViewSet, OSPFAreaViewSet, OSPFInterfaceViewSet, BGPRouterViewSet, \
- BGPScopeViewSet, BGPAddressFamilyViewSet, BGPSettingViewSet
+ BGPScopeViewSet, BGPAddressFamilyViewSet, BGPSettingViewSet, EIGRPRouterViewSet, EIGRPAddressFamilyViewSet, \
+ EIGRPNetworkViewSet, EIGRPInterfaceViewSet
router = NetBoxRouter()
router.register('staticroute', StaticRouteViewSet)
@@ -12,6 +13,10 @@
router.register('ospf-instance', OSPFInstanceViewSet)
router.register('ospf-area', OSPFAreaViewSet)
router.register('ospf-interface', OSPFInterfaceViewSet)
+router.register('eigrp-router', EIGRPRouterViewSet)
+router.register('eigrp-address-family', EIGRPAddressFamilyViewSet)
+router.register('eigrp-network', EIGRPNetworkViewSet)
+router.register('eigrp-interface', EIGRPInterfaceViewSet)
router.register('prefix-list', PrefixListViewSet)
router.register('prefix-list-entry', PrefixListEntryViewSet)
router.register('route-map', RouteMapViewSet)
diff --git a/netbox_routing/api/views/__init__.py b/netbox_routing/api/views/__init__.py
index e8b9bae..6d4c5f7 100644
--- a/netbox_routing/api/views/__init__.py
+++ b/netbox_routing/api/views/__init__.py
@@ -2,6 +2,10 @@
from .ospf import OSPFInstanceViewSet, OSPFAreaViewSet, OSPFInterfaceViewSet
from .bgp import BGPRouterViewSet, BGPScopeViewSet, BGPAddressFamilyViewSet, BGPSettingViewSet
from .objects import PrefixListViewSet, PrefixListEntryViewSet, RouteMapViewSet, RouteMapEntryViewSet
+from .eigrp import (
+ EIGRPRouterViewSet, EIGRPAddressFamilyViewSet,
+ EIGRPNetworkViewSet, EIGRPInterfaceViewSet
+)
__all__ = (
'StaticRouteViewSet',
@@ -11,6 +15,11 @@
'BGPAddressFamilyViewSet',
'BGPSettingViewSet',
+ 'EIGRPRouterViewSet',
+ 'EIGRPAddressFamilyViewSet',
+ 'EIGRPNetworkViewSet',
+ 'EIGRPInterfaceViewSet',
+
'OSPFInstanceViewSet',
'OSPFAreaViewSet',
'OSPFInterfaceViewSet',
diff --git a/netbox_routing/api/views/eigrp.py b/netbox_routing/api/views/eigrp.py
new file mode 100644
index 0000000..b19622f
--- /dev/null
+++ b/netbox_routing/api/views/eigrp.py
@@ -0,0 +1,41 @@
+from netbox.api.viewsets import NetBoxModelViewSet
+from netbox_routing import filtersets
+from netbox_routing.api.serializers import (
+ EIGRPRouterSerializer, EIGRPRouterSerializer, EIGRPAddressFamilySerializer,
+ EIGRPInterfaceSerializer, EIGRPNetworkSerializer
+)
+from netbox_routing.models import (
+ EIGRPRouter, EIGRPAddressFamily, EIGRPNetwork, EIGRPInterface
+)
+
+
+__all__ = (
+ 'EIGRPRouterViewSet',
+ 'EIGRPAddressFamilyViewSet',
+ 'EIGRPNetworkViewSet',
+ 'EIGRPInterfaceViewSet',
+)
+
+
+class EIGRPRouterViewSet(NetBoxModelViewSet):
+ queryset = EIGRPRouter.objects.all()
+ serializer_class = EIGRPRouterSerializer
+ filterset_class = filtersets.EIGRPRouterFilterSet
+
+
+class EIGRPAddressFamilyViewSet(NetBoxModelViewSet):
+ queryset = EIGRPAddressFamily.objects.all()
+ serializer_class = EIGRPAddressFamilySerializer
+ filterset_class = filtersets.EIGRPAddressFamilyFilterSet
+
+
+class EIGRPNetworkViewSet(NetBoxModelViewSet):
+ queryset = EIGRPNetwork.objects.all()
+ serializer_class = EIGRPNetworkSerializer
+ filterset_class = filtersets.EIGRPNetworkFilterSet
+
+
+class EIGRPInterfaceViewSet(NetBoxModelViewSet):
+ queryset = EIGRPInterface.objects.all()
+ serializer_class = EIGRPInterfaceSerializer
+ filterset_class = filtersets.EIGRPInterfaceFilterSet
diff --git a/netbox_routing/choices/eigrp.py b/netbox_routing/choices/eigrp.py
new file mode 100644
index 0000000..795aec3
--- /dev/null
+++ b/netbox_routing/choices/eigrp.py
@@ -0,0 +1,11 @@
+from utilities.choices import ChoiceSet
+
+
+class EIGRPRouterChoices(ChoiceSet):
+ CLASSIC = 'classic'
+ NAMED = 'named'
+
+ CHOICES = [
+ (CLASSIC, 'Classic Router'),
+ (NAMED, 'Named Router')
+ ]
\ No newline at end of file
diff --git a/netbox_routing/constants/eigrp.py b/netbox_routing/constants/eigrp.py
new file mode 100644
index 0000000..cee05c5
--- /dev/null
+++ b/netbox_routing/constants/eigrp.py
@@ -0,0 +1,11 @@
+from django.db.models import Q
+
+EIGRP_ROUTER_MODELS = Q(
+ app_label='netbox_routing',
+ model__in=('eigrpnamedrouter', 'eigrpclassicrouter', )
+)
+
+EIGRP_ASSIGNABLE_MODELS = Q(
+ app_label='netbox_routing',
+ model__in=('eigrpnamedrouter', 'eigrpclassicrouter', 'eigrpaddressfamily', )
+)
\ No newline at end of file
diff --git a/netbox_routing/fields/ip.py b/netbox_routing/fields/ip.py
index ac15df5..326f699 100644
--- a/netbox_routing/fields/ip.py
+++ b/netbox_routing/fields/ip.py
@@ -2,6 +2,7 @@
from django.db import models
from netaddr import AddrFormatError, IPAddress
+from ipam import lookups
from ipam.formfields import IPAddressFormField
@@ -14,7 +15,9 @@ def from_db_value(self, value, expression, connection):
return self.to_python(value)
def to_python(self, value):
- if not value:
+ if value is None:
+ return None
+ elif not value:
return value
try:
# Always return a netaddr.IPNetwork object. (netaddr.IPAddress does not provide a mask.)
@@ -25,6 +28,8 @@ def to_python(self, value):
raise ValidationError(e)
def get_prep_value(self, value):
+ if value is None:
+ return None
if isinstance(value, list):
return [str(self.to_python(v)) for v in value]
return str(self.to_python(value))
@@ -35,4 +40,16 @@ def form_class(self):
def formfield(self, **kwargs):
defaults = {'form_class': self.form_class()}
defaults.update(kwargs)
- return super().formfield(**defaults)
\ No newline at end of file
+ return super().formfield(**defaults)
+
+
+IPAddressField.register_lookup(lookups.IExact)
+IPAddressField.register_lookup(lookups.EndsWith)
+IPAddressField.register_lookup(lookups.IEndsWith)
+IPAddressField.register_lookup(lookups.StartsWith)
+IPAddressField.register_lookup(lookups.IStartsWith)
+IPAddressField.register_lookup(lookups.Regex)
+IPAddressField.register_lookup(lookups.IRegex)
+IPAddressField.register_lookup(lookups.NetContained)
+IPAddressField.register_lookup(lookups.NetContainedOrEqual)
+IPAddressField.register_lookup(lookups.NetFamily)
\ No newline at end of file
diff --git a/netbox_routing/filtersets/__init__.py b/netbox_routing/filtersets/__init__.py
index 8e81c05..819cdf6 100644
--- a/netbox_routing/filtersets/__init__.py
+++ b/netbox_routing/filtersets/__init__.py
@@ -2,6 +2,7 @@
from .objects import PrefixListFilterSet, PrefixListEntryFilterSet, RouteMapFilterSet, RouteMapEntryFilterSet
from .ospf import *
from .bgp import *
+from .eigrp import *
__all__ = (
@@ -14,6 +15,11 @@
'OSPFAreaFilterSet',
'OSPFInterfaceFilterSet',
+ 'EIGRPRouterFilterSet',
+ 'EIGRPAddressFamilyFilterSet',
+ 'EIGRPNetworkFilterSet',
+ 'EIGRPInterfaceFilterSet',
+
'PrefixListFilterSet',
'PrefixListEntryFilterSet',
'RouteMapFilterSet',
diff --git a/netbox_routing/filtersets/eigrp.py b/netbox_routing/filtersets/eigrp.py
new file mode 100644
index 0000000..4a2123e
--- /dev/null
+++ b/netbox_routing/filtersets/eigrp.py
@@ -0,0 +1,234 @@
+import django_filters
+import netaddr
+from django import forms
+from django.core.exceptions import ValidationError
+from django.db.models import Q
+from django.utils.translation import gettext as _
+
+from dcim.models import Device, Interface
+from ipam.models import Prefix
+from utilities.filters import MultiValueCharFilter
+
+from netbox.filtersets import NetBoxModelFilterSet
+from netbox_routing.models import EIGRPAddressFamily, EIGRPRouter, EIGRPNetwork, EIGRPInterface
+
+__all__ = (
+ 'EIGRPRouterFilterSet',
+ 'EIGRPAddressFamilyFilterSet',
+ 'EIGRPNetworkFilterSet',
+ 'EIGRPInterfaceFilterSet'
+)
+
+
+class RouterMixin:
+
+ def filter_rid(self, queryset, name, value):
+ try:
+ return queryset.filter(**{f'{name}__in': value})
+ except ValidationError:
+ return queryset.none()
+
+
+class EIGRPRouterFilterSet(RouterMixin, NetBoxModelFilterSet):
+ device_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='device',
+ queryset=Device.objects.all(),
+ label='Device (ID)',
+ )
+ device = django_filters.ModelMultipleChoiceFilter(
+ field_name='device__name',
+ queryset=Device.objects.all(),
+ to_field_name='name',
+ label='Device',
+ )
+ rid = MultiValueCharFilter(
+ method='filter_rid',
+ label=_('Router ID'),
+ )
+
+ class Meta:
+ model = EIGRPRouter
+ fields = ('device_id', 'device', 'mode', 'rid')
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ qs_filter = Q()
+ qs_filter |= Q(name__icontains=value)
+ qs_filter |= Q(device__name__icontains=value)
+ qs_filter |= Q(router_id__icontains=value)
+
+ return queryset.filter(qs_filter).distinct()
+
+
+class EIGRPAddressFamilyFilterSet(RouterMixin, NetBoxModelFilterSet):
+ router_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='router',
+ queryset=EIGRPRouter.objects.all(),
+ label='Router (ID)',
+ )
+ router = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__name',
+ queryset=EIGRPRouter.objects.all(),
+ to_field_name='name',
+ label='Router (Name)',
+ )
+ device_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__device',
+ queryset=Device.objects.all(),
+ label='Device (ID)',
+ )
+ device = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__device__name',
+ queryset=Device.objects.all(),
+ to_field_name='name',
+ label='Device',
+ )
+ rid = MultiValueCharFilter(
+ method='filter_rid',
+ label=_('Router ID'),
+ )
+
+ class Meta:
+ model = EIGRPAddressFamily
+ fields = ('router_id', 'router', 'device_id', 'device', 'rid', 'family' )
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ qs_filter = Q(
+ Q(rid__icontains=value)
+ )
+ qs_filter |= Q(eigrprouternamed__name__icontains=value)
+ return queryset.filter(qs_filter).distinct()
+
+
+class EIGRPNetworkFilterSet(NetBoxModelFilterSet):
+ router_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='router',
+ queryset=EIGRPRouter.objects.all(),
+ label='Router (ID)',
+ )
+ router = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__name',
+ queryset=EIGRPRouter.objects.all(),
+ to_field_name='name',
+ label='Router (Name)',
+ )
+ address_family_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='address_family',
+ queryset=EIGRPAddressFamily.objects.all(),
+ label='Address Family (ID)',
+ )
+ device_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__device',
+ queryset=Device.objects.all(),
+ label='Device (ID)',
+ )
+ device = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__device__name',
+ queryset=Device.objects.all(),
+ to_field_name='name',
+ label='Device',
+ )
+ network_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='network',
+ queryset=Prefix.objects.all(),
+ label='Network (ID)',
+ )
+ network = django_filters.CharFilter(
+ method='filter_prefix',
+ label=_('Network'),
+ )
+
+ class Meta:
+ model = EIGRPNetwork
+ fields = (
+ 'router_id', 'router', 'address_family_id', 'address_family', 'device_id', 'device', 'network_id',
+ 'network',
+ )
+
+ def filter_prefix(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ try:
+ query = str(netaddr.IPNetwork(value).cidr)
+ return queryset.filter(prefix=query)
+ except (netaddr.AddrFormatError, ValueError):
+ return queryset.none()
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ qs_filter = Q(
+ Q(eigrprouternamed__name__icontains=value) |
+ Q(address_family__rid__icontains=value)
+ )
+ qs_filter |= Q(network__contains=value.strip())
+ try:
+ prefix = str(netaddr.IPNetwork(value.strip()).cidr)
+ qs_filter |= Q(network__net_contains_or_equals=prefix)
+ except (netaddr.AddrFormatError, ValueError):
+ pass
+ return queryset.filter(qs_filter).distinct()
+
+
+class EIGRPInterfaceFilterSet(NetBoxModelFilterSet):
+ router_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='router',
+ queryset=EIGRPRouter.objects.all(),
+ label='Router (ID)',
+ )
+ router = django_filters.ModelMultipleChoiceFilter(
+ field_name='router__name',
+ queryset=EIGRPRouter.objects.all(),
+ to_field_name='name',
+ label='Router (Name)',
+ )
+ address_family_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='address_family',
+ queryset=EIGRPAddressFamily.objects.all(),
+ label='Address Family (ID)',
+ )
+ device_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='interface__device',
+ queryset=Device.objects.all(),
+ label='Device (ID)',
+ )
+ device = django_filters.ModelMultipleChoiceFilter(
+ field_name='interface__device__name',
+ queryset=Device.objects.all(),
+ to_field_name='name',
+ label='Device',
+ )
+ interface_id = django_filters.ModelMultipleChoiceFilter(
+ field_name='interface',
+ queryset=Interface.objects.all(),
+ label='Interface (ID)',
+ )
+ interface = django_filters.ModelMultipleChoiceFilter(
+ field_name='interface__name',
+ queryset=Interface.objects.all(),
+ to_field_name='name',
+ label='Interface',
+ )
+
+ class Meta:
+ model = EIGRPInterface
+ fields = (
+ 'router_id', 'router', 'address_family_id', 'address_family', 'device_id', 'device', 'interface_id',
+ 'interface', 'bfd', 'passive', 'authentication', 'passphrase'
+ )
+
+ def search(self, queryset, name, value):
+ if not value.strip():
+ return queryset
+ qs_filter = (
+ Q(router__name__icontains=value) |
+ Q(router__device__name__icontains=value) |
+ Q(interface__name__icontains=value) |
+ Q(interface__label__icontains=value) |
+ Q(interface__device__name__icontains=value)
+ )
+ return queryset.filter(qs_filter).distinct()
+
diff --git a/netbox_routing/forms/__init__.py b/netbox_routing/forms/__init__.py
index 686bae5..8d8ced7 100644
--- a/netbox_routing/forms/__init__.py
+++ b/netbox_routing/forms/__init__.py
@@ -5,8 +5,10 @@
from .ospf import OSPFAreaForm, OSPFInstanceForm, OSPFInterfaceForm
from .bgp import BGPRouterForm, BGPScopeForm, BGPAddressFamilyForm
from .static import StaticRouteForm
+from .eigrp import *
__all__ = (
+
# Static Routes
'StaticRouteForm',
'StaticRouteFilterForm',
@@ -27,6 +29,28 @@
'OSPFInterfaceBulkEditForm',
'OSPFInterfaceImportForm',
+ # EIGRP
+ 'EIGRPRouterForm',
+ 'EIGRPRouterBulkEditForm',
+ 'EIGRPRouterFilterForm',
+ 'EIGRPRouterImportForm',
+
+ 'EIGRPAddressFamilyForm',
+ 'EIGRPAddressFamilyBulkEditForm',
+ 'EIGRPAddressFamilyFilterForm',
+ 'EIGRPAddressFamilyImportForm',
+
+ 'EIGRPNetworkForm',
+ 'EIGRPNetworkBulkEditForm',
+ 'EIGRPNetworkFilterForm',
+ 'EIGRPNetworkImportForm',
+
+ 'EIGRPInterfaceForm',
+ 'EIGRPInterfaceBulkEditForm',
+ 'EIGRPInterfaceFilterForm',
+ 'EIGRPInterfaceImportForm',
+
+ # BGP
'BGPRouterForm',
'BGPScopeForm',
'BGPAddressFamilyForm',
diff --git a/netbox_routing/forms/bulk_edit/__init__.py b/netbox_routing/forms/bulk_edit/__init__.py
index 0cff3d5..fbd0dcc 100644
--- a/netbox_routing/forms/bulk_edit/__init__.py
+++ b/netbox_routing/forms/bulk_edit/__init__.py
@@ -1,6 +1,7 @@
from .static import *
from .objects import *
from .ospf import *
+from .eigrp import *
__all__ = (
@@ -12,6 +13,12 @@
'OSPFInterfaceBulkEditForm',
'OSPFAreaBulkEditForm',
+ # EIGRP
+ 'EIGRPRouterBulkEditForm',
+ 'EIGRPAddressFamilyBulkEditForm',
+ 'EIGRPNetworkBulkEditForm',
+ 'EIGRPInterfaceBulkEditForm',
+
# Route Objects
'PrefixListEntryBulkEditForm',
'RouteMapEntryBulkEditForm'
diff --git a/netbox_routing/forms/bulk_edit/eigrp.py b/netbox_routing/forms/bulk_edit/eigrp.py
new file mode 100644
index 0000000..e59b2a7
--- /dev/null
+++ b/netbox_routing/forms/bulk_edit/eigrp.py
@@ -0,0 +1,132 @@
+from django import forms
+from django.forms import CharField
+from django.utils.translation import gettext as _
+
+from dcim.models import Device
+from ipam.models import VRF
+from netbox.forms import NetBoxModelBulkEditForm
+from netbox_routing.models import EIGRPRouter, EIGRPAddressFamily, EIGRPNetwork, EIGRPInterface
+from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
+from utilities.forms.fields import DynamicModelChoiceField, CommentField
+from utilities.forms.rendering import FieldSet
+
+from netbox_routing import choices
+
+__all__ = (
+ 'EIGRPRouterBulkEditForm',
+ 'EIGRPAddressFamilyBulkEditForm',
+ 'EIGRPNetworkBulkEditForm',
+ 'EIGRPInterfaceBulkEditForm',
+)
+
+
+class EIGRPRouterBulkEditForm(NetBoxModelBulkEditForm):
+ device = DynamicModelChoiceField(
+ queryset=Device.objects.all(),
+ label=_('Device'),
+ required=False,
+ selector=True
+ )
+
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+ comments = CommentField()
+
+ model = EIGRPRouter
+ fieldsets = (
+ FieldSet('device', 'mode', name='EIGRP'),
+ FieldSet('description', ),
+ )
+ nullable_fields = ()
+
+
+class EIGRPAddressFamilyBulkEditForm(NetBoxModelBulkEditForm):
+ vrf = DynamicModelChoiceField(
+ queryset=VRF.objects.all(),
+ label=_('VRF'),
+ required=False,
+ selector=True
+ )
+ family = CharField(max_length=4)
+
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+ comments = CommentField()
+
+ model = EIGRPAddressFamily
+ fieldsets = (
+ FieldSet('description'),
+ )
+ nullable_fields = ()
+
+
+class EIGRPNetworkBulkEditForm(NetBoxModelBulkEditForm):
+ router = DynamicModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ label=_('Router'),
+ required=False,
+ selector=True
+ )
+ address_family = DynamicModelChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ label=_('Router'),
+ required=False,
+ selector=True
+ )
+
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+ comments = CommentField()
+
+ model = EIGRPNetwork
+ fieldsets = (
+ FieldSet('description'),
+ )
+ nullable_fields = ()
+
+
+class EIGRPInterfaceBulkEditForm(NetBoxModelBulkEditForm):
+ router = DynamicModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ label=_('EIGRP Router'),
+ required=False,
+ selector=True
+ )
+ address_family = DynamicModelChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ label=_('EIGRP Address Family'),
+ required=False,
+ selector=True
+ )
+ passive = forms.ChoiceField(label=_('Passive'), choices=BOOLEAN_WITH_BLANK_CHOICES, required=False)
+ bfd = forms.ChoiceField(label=_('BFD'), choices=BOOLEAN_WITH_BLANK_CHOICES, required=False)
+ authentication = forms.ChoiceField(
+ label=_('Authentication'),
+ choices=add_blank_choice(choices.AuthenticationChoices),
+ required=False
+ )
+ passphrase = forms.CharField(label=_('Passphrase'), required=False)
+
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+ comments = CommentField()
+
+ model = EIGRPInterface
+ fieldsets = (
+ FieldSet('router', 'address_family', name='EIGRP'),
+ FieldSet('priority', 'bfd', 'authentication', 'passphrase', name='Attributes'),
+ FieldSet('description'),
+ )
+ nullable_fields = ()
diff --git a/netbox_routing/forms/bulk_import/__init__.py b/netbox_routing/forms/bulk_import/__init__.py
index fb69772..a9efb2f 100644
--- a/netbox_routing/forms/bulk_import/__init__.py
+++ b/netbox_routing/forms/bulk_import/__init__.py
@@ -1,4 +1,5 @@
from .ospf import *
+from .eigrp import *
__all__ = (
@@ -6,5 +7,11 @@
'OSPFInstanceImportForm',
'OSPFAreaImportForm',
'OSPFInterfaceImportForm',
+
+ # EIGRP
+ 'EIGRPRouterImportForm',
+ 'EIGRPAddressFamilyImportForm',
+ 'EIGRPNetworkImportForm',
+ 'EIGRPInterfaceImportForm',
)
diff --git a/netbox_routing/forms/bulk_import/eigrp.py b/netbox_routing/forms/bulk_import/eigrp.py
new file mode 100644
index 0000000..7e0d96b
--- /dev/null
+++ b/netbox_routing/forms/bulk_import/eigrp.py
@@ -0,0 +1,82 @@
+from django.utils.translation import gettext as _
+
+from dcim.models import Interface
+from ipam.models import Prefix
+from netbox.forms import NetBoxModelImportForm
+from utilities.forms.fields import CSVModelChoiceField
+
+from netbox_routing.models import *
+
+
+__all__ = (
+ 'EIGRPRouterImportForm',
+ 'EIGRPAddressFamilyImportForm',
+ 'EIGRPNetworkImportForm',
+ 'EIGRPInterfaceImportForm',
+)
+
+
+class EIGRPRouterImportForm(NetBoxModelImportForm):
+ device = CSVModelChoiceField(
+ queryset=Interface.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text=_('Name of device')
+ )
+
+ class Meta:
+ model = EIGRPRouter
+ fields = ('device', 'rid', 'mode', 'name', 'pid', 'description', 'comments', 'tags',)
+
+
+class EIGRPAddressFamilyImportForm(NetBoxModelImportForm):
+
+ class Meta:
+ model = EIGRPAddressFamily
+ fields = ('router', 'family', 'rid', 'description', 'comments', 'tags',)
+
+
+class EIGRPNetworkImportForm(NetBoxModelImportForm):
+ router = CSVModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ required=True,
+ help_text=_('PK of Router Instance')
+ )
+ address_family = CSVModelChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ required=False,
+ help_text=_('PK of Address Family')
+ )
+ network = CSVModelChoiceField(
+ queryset=Prefix.objects.all(),
+ required=True,
+ to_field_name='prefix',
+ help_text=_('Prefix of Network')
+ )
+
+ class Meta:
+ model = EIGRPNetwork
+ fields = ('router', 'address_family', 'network', 'description', 'comments', 'tags',)
+
+
+class EIGRPInterfaceImportForm(NetBoxModelImportForm):
+ router = CSVModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ required=True,
+ help_text=_('PK of Router Instance')
+ )
+ address_family = CSVModelChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ required=False,
+ help_text=_('PK of Address Family')
+ )
+ interface = CSVModelChoiceField(
+ queryset=Interface.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text=_('Name of interface')
+ )
+
+ class Meta:
+ model = EIGRPInterface
+ fields = ('router', 'address_family', 'interface', 'description', 'comments', 'tags',)
diff --git a/netbox_routing/forms/eigrp.py b/netbox_routing/forms/eigrp.py
new file mode 100644
index 0000000..b051af3
--- /dev/null
+++ b/netbox_routing/forms/eigrp.py
@@ -0,0 +1,186 @@
+from django import forms
+from django.core.exceptions import ValidationError
+from django.forms import ChoiceField
+from django.utils.translation import gettext as _
+
+from dcim.models import Interface, Device
+from ipam.choices import IPAddressFamilyChoices
+from ipam.models import VRF, Prefix
+from netbox.forms import NetBoxModelForm
+from netbox_routing.choices.eigrp import EIGRPRouterChoices
+from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, get_field_value
+from utilities.forms.fields import DynamicModelChoiceField, CommentField
+from utilities.forms.widgets import HTMXSelect
+
+from netbox_routing.models import *
+
+
+__all__ = (
+ 'EIGRPRouterForm',
+ 'EIGRPAddressFamilyForm',
+ 'EIGRPNetworkForm',
+ 'EIGRPInterfaceForm',
+)
+
+
+class EIGRPRouterForm(NetBoxModelForm):
+ device = DynamicModelChoiceField(
+ queryset=Device.objects.all(),
+ required=True,
+ selector=True,
+ label=_('Device'),
+ )
+ comments = CommentField()
+
+ class Meta:
+ model = EIGRPRouter
+ fields = ('device', 'mode', 'name', 'pid', 'rid', 'description', 'comments', )
+ widgets = {
+ 'mode': HTMXSelect(),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ mode = get_field_value(self, 'mode')
+ if mode == EIGRPRouterChoices.NAMED:
+ del self.fields['pid']
+ elif mode == EIGRPRouterChoices.CLASSIC:
+ del self.fields['name']
+
+
+class EIGRPAddressFamilyForm(NetBoxModelForm):
+ router = DynamicModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ required=True,
+ selector=True,
+ label=_('Router'),
+ )
+ vrf = DynamicModelChoiceField(
+ queryset=VRF.objects.all(),
+ required=False,
+ selector=True,
+ label=_('VRF'),
+ )
+ family = ChoiceField(
+ required=True,
+ choices=IPAddressFamilyChoices,
+ label=_('Family'),
+ )
+ comments = CommentField()
+
+
+ class Meta:
+ model = EIGRPAddressFamily
+ fields = (
+ 'router', 'vrf', 'family', 'rid', 'description', 'comments',
+ )
+
+
+class EIGRPNetworkForm(NetBoxModelForm):
+ router = DynamicModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ required=True,
+ selector=True,
+ label=_('Router'),
+ )
+ address_family = DynamicModelChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Address Family'),
+ )
+ network = DynamicModelChoiceField(
+ queryset=Prefix.objects.all(),
+ required=True,
+ selector=True,
+ label=_('Prefix'),
+ )
+ comments = CommentField()
+
+
+ class Meta:
+ model = EIGRPNetwork
+ fields = (
+ 'router', 'address_family', 'network', 'description', 'comments',
+ )
+
+
+class EIGRPInterfaceForm(NetBoxModelForm):
+ device = DynamicModelChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Device'),
+ )
+ router = DynamicModelChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ required=True,
+ selector=True,
+ label=_('EIGRP Router'),
+ query_params={
+ 'device_id': '$device',
+ }
+ )
+ address_family = DynamicModelChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Address Family'),
+ query_params={
+ 'router_id': '$router',
+ }
+ )
+ interface = DynamicModelChoiceField(
+ queryset=Interface.objects.all(),
+ required=True,
+ selector=True,
+ label=_('Interface'),
+ query_params={
+ 'device_id': '$device',
+ }
+ )
+ passive = forms.BooleanField(
+ required=False,
+ label='Passive Interface',
+ widget=forms.Select(
+ choices=BOOLEAN_WITH_BLANK_CHOICES
+ )
+ )
+ comments = CommentField()
+
+
+ class Meta:
+ model = EIGRPInterface
+ fields = (
+ 'device', 'router', 'address_family', 'interface', 'passive', 'bfd', 'authentication', 'passphrase',
+ 'description', 'comments',
+ )
+
+ widgets = {
+ 'bfd': forms.Select(choices=BOOLEAN_WITH_BLANK_CHOICES),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ if self.instance.pk:
+ self.initial['device'] = self.instance.interface.device.pk
+
+ def clean(self):
+ super().clean()
+ if self.cleaned_data.get('router') and self.cleaned_data.get('address_family'):
+ if self.cleaned_data.get('router') != self.cleaned_data.get('address_family').router:
+ raise ValidationError(
+ {
+ 'router': _('EIGRP Router and EIGRP Address Family Router must match'),
+ 'interface': _('EIGRP Router and EIGRP Address Family Router must match')
+ }
+ )
+ if self.cleaned_data.get('router') and self.cleaned_data.get('interface'):
+ if self.cleaned_data.get('router').device != self.cleaned_data.get('interface').device:
+ raise ValidationError(
+ {
+ 'router': _('EIGRP Router Device and Interface Device must match'),
+ 'interface': _('EIGRP Interface Device and Interface Device must match')
+ }
+ )
diff --git a/netbox_routing/forms/filtersets/__init__.py b/netbox_routing/forms/filtersets/__init__.py
index 627bc7d..019cd7f 100644
--- a/netbox_routing/forms/filtersets/__init__.py
+++ b/netbox_routing/forms/filtersets/__init__.py
@@ -3,6 +3,7 @@
from .ospf import OSPFAreaFilterForm, OSPFInstanceFilterForm, OSPFInterfaceFilterForm
from .objects import PrefixListFilterForm, PrefixListEntryFilterForm, RouteMapFilterForm,\
RouteMapEntryFilterForm
+from .eigrp import *
__all__ = (
# Static
@@ -14,6 +15,12 @@
'BGPAddressFamilyFilterForm',
'BGPSettingFilterForm',
+ # EIGRP
+ 'EIGRPRouterFilterForm',
+ 'EIGRPAddressFamilyFilterForm',
+ 'EIGRPNetworkFilterForm',
+ 'EIGRPInterfaceFilterForm',
+
# OSPF
'OSPFAreaFilterForm',
'OSPFInstanceFilterForm',
diff --git a/netbox_routing/forms/filtersets/eigrp.py b/netbox_routing/forms/filtersets/eigrp.py
new file mode 100644
index 0000000..cf8e0b3
--- /dev/null
+++ b/netbox_routing/forms/filtersets/eigrp.py
@@ -0,0 +1,180 @@
+from django import forms
+from django.utils.translation import gettext as _
+
+from dcim.models import Interface, Device
+from ipam.choices import IPAddressFamilyChoices
+from ipam.forms.filtersets import PREFIX_MASK_LENGTH_CHOICES
+from ipam.models import VRF, Prefix
+from netbox.forms import NetBoxModelFilterSetForm
+from netbox_routing.choices import AuthenticationChoices
+from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
+from utilities.forms.fields import TagFilterField, DynamicModelMultipleChoiceField, DynamicModelChoiceField
+from utilities.forms.rendering import FieldSet
+
+from netbox_routing.models import EIGRPRouter, EIGRPAddressFamily, EIGRPNetwork, EIGRPInterface
+
+
+__all__ = (
+ 'EIGRPRouterFilterForm',
+ 'EIGRPAddressFamilyFilterForm',
+ 'EIGRPNetworkFilterForm',
+ 'EIGRPInterfaceFilterForm',
+)
+
+
+class EIGRPRouterFilterForm(NetBoxModelFilterSetForm):
+ model = EIGRPRouter
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag', 'device', 'mode'),
+ )
+
+ device_id = DynamicModelMultipleChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Device'),
+ )
+ tag = TagFilterField(model)
+
+
+class EIGRPAddressFamilyFilterForm(NetBoxModelFilterSetForm):
+ model = EIGRPAddressFamily
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'router_id', 'vrf_id', 'family', 'tag'),
+ )
+
+ router_id = DynamicModelMultipleChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ selector=True,
+ label=_('EIGRP Router'),
+ )
+ vrf_id = DynamicModelMultipleChoiceField(
+ queryset=VRF.objects.all(),
+ required=False,
+ selector=True,
+ label=_('VRF'),
+ )
+ family = forms.ChoiceField(
+ required=False,
+ choices=add_blank_choice(IPAddressFamilyChoices),
+ label=_('Address family')
+ )
+ tag = TagFilterField(model)
+
+
+class EIGRPNetworkFilterForm(NetBoxModelFilterSetForm):
+ model = EIGRPNetwork
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('router_id', 'address_family', name=_('EIGRP')),
+ FieldSet(
+ 'prefix_id', 'vrf_id', 'present_in_vrf_id', 'family', 'mask_length__lte', 'within_include',
+ 'mask_length', name=_('Prefix')
+ ),
+ )
+ prefix_id = DynamicModelMultipleChoiceField(
+ queryset=Prefix.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Prefix'),
+ )
+ router_id = DynamicModelMultipleChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ selector=True,
+ label=_('EIGRP Router'),
+ )
+ address_family_id = DynamicModelMultipleChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ selector=True,
+ label=_('EIGRP Address Family'),
+ )
+ vrf_id = DynamicModelMultipleChoiceField(
+ queryset=VRF.objects.all(),
+ required=False,
+ label=_('Assigned VRF'),
+ null_option='Global'
+ )
+ present_in_vrf_id = DynamicModelChoiceField(
+ queryset=VRF.objects.all(),
+ required=False,
+ label=_('Present in VRF')
+ )
+ family = forms.ChoiceField(
+ required=False,
+ choices=add_blank_choice(IPAddressFamilyChoices),
+ label=_('Address family')
+ )
+ mask_length__lte = forms.IntegerField(
+ widget=forms.HiddenInput()
+ )
+ within_include = forms.CharField(
+ required=False,
+ widget=forms.TextInput(
+ attrs={
+ 'placeholder': 'Prefix',
+ }
+ ),
+ label=_('Search within')
+ )
+ mask_length = forms.MultipleChoiceField(
+ required=False,
+ choices=PREFIX_MASK_LENGTH_CHOICES,
+ label=_('Mask length')
+ )
+ tag = TagFilterField(model)
+
+
+class EIGRPInterfaceFilterForm(NetBoxModelFilterSetForm):
+ model = EIGRPInterface
+ fieldsets = (
+ FieldSet('q', 'filter_id', 'tag'),
+ FieldSet('router_id', 'address_family_id', name=_('EIGRP')),
+ FieldSet('device_id', 'interface_id', name=_('Device')),
+ FieldSet('passive', 'bfd', 'authentication', name=_('Attributes'))
+ )
+ router_id = DynamicModelMultipleChoiceField(
+ queryset=EIGRPRouter.objects.all(),
+ required=False,
+ selector=True,
+ label=_('EIGRP Router'),
+ )
+ address_family_id = DynamicModelMultipleChoiceField(
+ queryset=EIGRPAddressFamily.objects.all(),
+ required=False,
+ selector=True,
+ label=_('EIGRP Address Family'),
+ )
+ device_id = DynamicModelMultipleChoiceField(
+ queryset=Device.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Device'),
+ )
+ interface_id = DynamicModelMultipleChoiceField(
+ queryset=Interface.objects.all(),
+ required=False,
+ selector=True,
+ label=_('Interface'),
+ )
+ bfd = forms.NullBooleanField(
+ required=False,
+ label='BFD Enabled',
+ widget=forms.Select(
+ choices=BOOLEAN_WITH_BLANK_CHOICES
+ )
+ )
+ passive = forms.BooleanField(
+ required=False,
+ label='Passive Interface',
+ widget=forms.Select(
+ choices=BOOLEAN_WITH_BLANK_CHOICES
+ )
+ )
+ authentication = forms.ChoiceField(
+ choices=add_blank_choice(AuthenticationChoices),
+ required=False
+ )
+ tag = TagFilterField(model)
diff --git a/netbox_routing/graphql/filters.py b/netbox_routing/graphql/filters.py
index 62468f3..3f1f4ce 100644
--- a/netbox_routing/graphql/filters.py
+++ b/netbox_routing/graphql/filters.py
@@ -1,17 +1,17 @@
-from typing import Annotated
-
-import strawberry
import strawberry_django
+from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from netbox_routing import filtersets, models
-from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
-
__all__ = (
'StaticRouteFilter',
'OSPFInstanceFilter',
'OSPFAreaFilter',
'OSPFInterfaceFilter',
+ 'EIGRPRouterFilter',
+ 'EIGRPAddressFamilyFilter',
+ 'EIGRPNetworkFilter',
+ 'EIGRPInterfaceFilter',
)
@@ -38,3 +38,27 @@ class OSPFAreaFilter(BaseFilterMixin):
@autotype_decorator(filtersets.OSPFInterfaceFilterSet)
class OSPFInterfaceFilter(BaseFilterMixin):
pass
+
+
+@strawberry_django.filter(models.EIGRPRouter, lookups=True)
+@autotype_decorator(filtersets.EIGRPRouterFilterSet)
+class EIGRPRouterFilter(BaseFilterMixin):
+ rid: str
+
+
+@strawberry_django.filter(models.EIGRPAddressFamily, lookups=True)
+@autotype_decorator(filtersets.EIGRPAddressFamilyFilterSet)
+class EIGRPAddressFamilyFilter(BaseFilterMixin):
+ rid: str
+
+
+@strawberry_django.filter(models.EIGRPNetwork, lookups=True)
+@autotype_decorator(filtersets.EIGRPNetworkFilterSet)
+class EIGRPNetworkFilter(BaseFilterMixin):
+ pass
+
+
+@strawberry_django.filter(models.EIGRPInterface, lookups=True)
+@autotype_decorator(filtersets.EIGRPInterfaceFilterSet)
+class EIGRPInterfaceFilter(BaseFilterMixin):
+ pass
diff --git a/netbox_routing/graphql/schema.py b/netbox_routing/graphql/schema.py
index 07a73b2..57222b9 100644
--- a/netbox_routing/graphql/schema.py
+++ b/netbox_routing/graphql/schema.py
@@ -24,7 +24,23 @@ class OSPFQuery:
ospf_interface_list: List[OSPFInterfaceType] = strawberry_django.field()
+@strawberry.type(name="Query")
+class EIGRPQuery:
+ eigrp_router: EIGRPRouterType = strawberry_django.field()
+ eigrp_router_list: List[EIGRPRouterType] = strawberry_django.field()
+
+ eigrp_address_family: EIGRPAddressFamilyType = strawberry_django.field()
+ eigrp_address_family_list: List[EIGRPAddressFamilyType] = strawberry_django.field()
+
+ eigrp_network: EIGRPNetworkType = strawberry_django.field()
+ eigrp_network_list: List[EIGRPNetworkType] = strawberry_django.field()
+
+ eigrp_interface: EIGRPInterfaceType = strawberry_django.field()
+ eigrp_interface_list: List[EIGRPInterfaceType] = strawberry_django.field()
+
+
schema = [
StaticRouteQuery,
- OSPFQuery
+ OSPFQuery,
+ EIGRPQuery,
]
diff --git a/netbox_routing/graphql/types.py b/netbox_routing/graphql/types.py
index ca74368..e694ab2 100644
--- a/netbox_routing/graphql/types.py
+++ b/netbox_routing/graphql/types.py
@@ -1,4 +1,4 @@
-from typing import Annotated, List
+from typing import Annotated, List, Union
import strawberry
import strawberry_django
@@ -10,9 +10,15 @@
__all__ = (
'StaticRouteType',
+
'OSPFInstanceType',
'OSPFAreaType',
'OSPFInterfaceType',
+
+ 'EIGRPRouterType',
+ 'EIGRPAddressFamilyType',
+ 'EIGRPNetworkType',
+ 'EIGRPInterfaceType',
)
@@ -70,3 +76,56 @@ class OSPFInterfaceType(NetBoxObjectType):
authentication: str | None
passphrase: str | None
+
+@strawberry_django.type(
+ models.EIGRPRouter,
+ fields='__all__',
+ filters=EIGRPRouterFilter
+)
+class EIGRPRouterType(NetBoxObjectType):
+
+ device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]
+ rid: str
+ type: str
+ name: str
+ pid: str
+
+
+@strawberry_django.type(
+ models.EIGRPAddressFamily,
+ fields='__all__',
+ filters=EIGRPAddressFamilyFilter
+)
+class EIGRPAddressFamilyType(NetBoxObjectType):
+
+ router: Annotated["EIGRPRouterType", strawberry.lazy('netbox_routing.graphql.types')]
+ rid: str
+
+
+@strawberry_django.type(
+ models.EIGRPNetwork,
+ fields='__all__',
+ filters=EIGRPNetworkFilter
+)
+class EIGRPNetworkType(NetBoxObjectType):
+
+ router: Annotated["EIGRPRouterType", strawberry.lazy('netbox_routing.graphql.types')]
+ address_family: Annotated["EIGRPAddressFamilyType", strawberry.lazy('netbox_routing.graphql.types')] | None
+ network: Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]
+
+
+@strawberry_django.type(
+ models.EIGRPInterface,
+ fields='__all__',
+ filters=EIGRPInterfaceFilter
+)
+class EIGRPInterfaceType(NetBoxObjectType):
+
+ router: Annotated["EIGRPRouterType", strawberry.lazy('netbox_routing.graphql.types')]
+ address_family: Annotated["EIGRPAddressFamilyType", strawberry.lazy('netbox_routing.graphql.types')] | None
+ interface: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
+ passive: str | None
+ bfd: bool | None
+ authentication: str | None
+ passphrase: str | None
+
diff --git a/netbox_routing/migrations/0010_eigrp.py b/netbox_routing/migrations/0010_eigrp.py
new file mode 100644
index 0000000..8b2a214
--- /dev/null
+++ b/netbox_routing/migrations/0010_eigrp.py
@@ -0,0 +1,256 @@
+# Generated by Django 5.0.8 on 2024-09-27 13:00
+
+import django.db.models.deletion
+import netbox_routing.fields.ip
+import taggit.managers
+import utilities.json
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dcim', '0190_nested_modules'),
+ ('extras', '0121_customfield_related_object_filter'),
+ ('ipam', '0070_vlangroup_vlan_id_ranges'),
+ ('netbox_routing', '0009_alter_staticroute_metric_alter_staticroute_permanent'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='EIGRPAddressFamily',
+ 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=utilities.json.CustomFieldJSONEncoder,
+ ),
+ ),
+ ('description', models.CharField(blank=True, max_length=200)),
+ ('comments', models.TextField(blank=True)),
+ ('family', models.PositiveSmallIntegerField()),
+ ('rid', netbox_routing.fields.ip.IPAddressField(blank=True, null=True)),
+ (
+ 'tags',
+ taggit.managers.TaggableManager(
+ through='extras.TaggedItem', to='extras.Tag'
+ ),
+ ),
+ (
+ 'vrf',
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name='eigrp_address_families',
+ to='ipam.vrf',
+ ),
+ ),
+ ],
+ options={
+ 'verbose_name': 'EIGRP Address Family',
+ },
+ ),
+ migrations.CreateModel(
+ name='EIGRPRouter',
+ 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=utilities.json.CustomFieldJSONEncoder,
+ ),
+ ),
+ ('description', models.CharField(blank=True, max_length=200)),
+ ('comments', models.TextField(blank=True)),
+ ('mode', models.CharField(max_length=10)),
+ ('name', models.CharField(blank=True, max_length=100, null=True)),
+ ('pid', models.PositiveIntegerField(blank=True, null=True)),
+ ('rid', netbox_routing.fields.ip.IPAddressField(blank=True, null=True)),
+ (
+ 'device',
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name='eigrp',
+ to='dcim.device',
+ ),
+ ),
+ (
+ 'tags',
+ taggit.managers.TaggableManager(
+ through='extras.TaggedItem', to='extras.Tag'
+ ),
+ ),
+ ],
+ options={
+ 'verbose_name': 'EIGRP Router',
+ },
+ ),
+ migrations.CreateModel(
+ name='EIGRPNetwork',
+ 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=utilities.json.CustomFieldJSONEncoder,
+ ),
+ ),
+ ('description', models.CharField(blank=True, max_length=200)),
+ ('comments', models.TextField(blank=True)),
+ (
+ 'address_family',
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to='netbox_routing.eigrpaddressfamily',
+ ),
+ ),
+ (
+ 'network',
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name='eigrp',
+ to='ipam.prefix',
+ ),
+ ),
+ (
+ 'tags',
+ taggit.managers.TaggableManager(
+ through='extras.TaggedItem', to='extras.Tag'
+ ),
+ ),
+ (
+ 'router',
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to='netbox_routing.eigrprouter',
+ ),
+ ),
+ ],
+ options={
+ 'verbose_name': 'EIGRP Network',
+ },
+ ),
+ migrations.CreateModel(
+ name='EIGRPInterface',
+ 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=utilities.json.CustomFieldJSONEncoder,
+ ),
+ ),
+ ('description', models.CharField(blank=True, max_length=200)),
+ ('comments', models.TextField(blank=True)),
+ ('passive', models.BooleanField()),
+ ('bfd', models.BooleanField()),
+ (
+ 'authentication',
+ models.CharField(blank=True, max_length=50, null=True),
+ ),
+ ('passphrase', models.CharField(blank=True, max_length=200, null=True)),
+ (
+ 'address_family',
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to='netbox_routing.eigrpaddressfamily',
+ ),
+ ),
+ (
+ 'interface',
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name='eigrp',
+ to='dcim.interface',
+ ),
+ ),
+ (
+ 'tags',
+ taggit.managers.TaggableManager(
+ through='extras.TaggedItem', to='extras.Tag'
+ ),
+ ),
+ (
+ 'router',
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to='netbox_routing.eigrprouter',
+ ),
+ ),
+ ],
+ options={
+ 'verbose_name': 'EIGRP Interface',
+ },
+ ),
+ migrations.AddField(
+ model_name='eigrpaddressfamily',
+ name='router',
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to='netbox_routing.eigrprouter',
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name='eigrpnetwork',
+ constraint=models.UniqueConstraint(
+ fields=('router', 'address_family', 'network'),
+ name='netbox_routing_eigrpnetwork_unique_network',
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name='eigrpinterface',
+ constraint=models.UniqueConstraint(
+ fields=('router', 'address_family', 'interface'),
+ name='netbox_routing_eigrpinterface_unique_interface',
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name='eigrpaddressfamily',
+ constraint=models.UniqueConstraint(
+ fields=('router', 'vrf', 'family'),
+ name='netbox_routing_eigrpaddressfamily_unique_af',
+ ),
+ ),
+ ]
diff --git a/netbox_routing/models/__init__.py b/netbox_routing/models/__init__.py
index 52e9c90..c7eb5f5 100644
--- a/netbox_routing/models/__init__.py
+++ b/netbox_routing/models/__init__.py
@@ -2,16 +2,26 @@
from .ospf import OSPFArea, OSPFInstance, OSPFInterface
from .objects import PrefixList, PrefixListEntry, RouteMap, RouteMapEntry
from .bgp import BGPRouter, BGPScope, BGPAddressFamily, BGPSetting
+from .eigrp import *
__all__ = (
'StaticRoute',
+
'OSPFArea',
'OSPFInstance',
'OSPFInterface',
+
+ 'EIGRPRouter',
+ 'EIGRPAddressFamily',
+ 'EIGRPNetwork',
+ 'EIGRPInterface',
+
'PrefixList',
'PrefixListEntry',
'RouteMap',
'RouteMapEntry',
+
+ # Not fully implemented
'BGPRouter',
'BGPScope',
'BGPAddressFamily',
diff --git a/netbox_routing/models/eigrp.py b/netbox_routing/models/eigrp.py
new file mode 100644
index 0000000..7ddfa39
--- /dev/null
+++ b/netbox_routing/models/eigrp.py
@@ -0,0 +1,216 @@
+from django.db import models
+from django.urls import reverse
+from django.utils.translation import gettext as _
+
+from ipam.choices import IPAddressFamilyChoices
+from netbox.models import PrimaryModel
+from netbox_routing import choices
+from netbox_routing.choices.eigrp import EIGRPRouterChoices
+from netbox_routing.fields.ip import IPAddressField
+
+__all__ = (
+ 'EIGRPRouter',
+ 'EIGRPAddressFamily',
+ 'EIGRPNetwork',
+ 'EIGRPInterface',
+)
+
+
+class EIGRPRouter(PrimaryModel):
+ device = models.ForeignKey(
+ verbose_name=_('Device'),
+ to='dcim.Device',
+ related_name='eigrp',
+ on_delete=models.CASCADE,
+ blank=False,
+ null=False
+ )
+ mode = models.CharField(
+ verbose_name=_('Mode'),
+ max_length=10,
+ choices=EIGRPRouterChoices
+ )
+ name = models.CharField(
+ verbose_name=_('Name'),
+ max_length=100,
+ blank=True,
+ null=True
+ )
+ pid = models.PositiveIntegerField(
+ verbose_name=_('Process ID'),
+ blank=True,
+ null=True
+ )
+ rid = IPAddressField(
+ verbose_name=_('Router ID'),
+ blank=True,
+ null=True
+ )
+
+ class Meta:
+ verbose_name = 'EIGRP Router'
+
+ def __str__(self):
+ if self.pid:
+ return f'Process {self.pid} ({self.rid})'
+ return f'{self.name} ({self.rid})'
+
+ def get_absolute_url(self):
+ return reverse('plugins:netbox_routing:eigrprouter', args=[self.pk])
+
+ @property
+ def identifier(self):
+ if self.name:
+ return f'{self.name}'
+ elif self.pid:
+ return f'{self.pid}'
+ return f'{self}'
+
+
+class EIGRPAddressFamily(PrimaryModel):
+ router = models.ForeignKey(
+ verbose_name=_('Router'),
+ to=EIGRPRouter,
+ on_delete=models.CASCADE,
+ blank=False,
+ null=False
+ )
+ vrf = models.ForeignKey(
+ verbose_name=_('VRF'),
+ to='ipam.VRF',
+ related_name='eigrp_address_families',
+ on_delete=models.CASCADE,
+ blank=True,
+ null=True
+ )
+ family = models.PositiveSmallIntegerField(
+ verbose_name=_('Address Family'),
+ choices=IPAddressFamilyChoices,
+ blank=False,
+ null=False
+ )
+ rid = IPAddressField(
+ verbose_name=_('Router ID'),
+ blank=True,
+ null=True
+ )
+
+ class Meta:
+ verbose_name = 'EIGRP Address Family'
+ constraints = (
+ models.UniqueConstraint(
+ fields=('router', 'vrf', 'family'),
+ name='%(app_label)s_%(class)s_unique_af'
+ ),
+ )
+
+ def __str__(self):
+ if self.vrf:
+ return f'{self.router.identifier} ({self.family} vrf {self.vrf.name})'
+ return f'{self.router.identifier} ({self.family})'
+
+ def get_absolute_url(self):
+ return reverse('plugins:netbox_routing:eigrpaddressfamily', args=[self.pk])
+
+
+class EIGRPNetwork(PrimaryModel):
+ router = models.ForeignKey(
+ verbose_name=_('Router'),
+ to=EIGRPRouter,
+ on_delete=models.CASCADE,
+ blank=False,
+ null=False
+ )
+ address_family = models.ForeignKey(
+ verbose_name=_('Address Family'),
+ to=EIGRPAddressFamily,
+ on_delete=models.CASCADE,
+ blank=True,
+ null=True
+ )
+ network = models.ForeignKey(
+ verbose_name=_('Network'),
+ to='ipam.Prefix',
+ related_name='eigrp',
+ on_delete=models.CASCADE,
+ blank=False,
+ null=False
+ )
+
+ class Meta:
+ verbose_name = 'EIGRP Network'
+ constraints = (
+ models.UniqueConstraint(
+ fields=('router', 'address_family', 'network',),
+ name='%(app_label)s_%(class)s_unique_network'
+ ),
+ )
+
+ def __str__(self):
+ if self.address_family:
+ return f'{self.network} ({self.router.identifier} {self.address_family})'
+ return f'{self.network} ({self.router.identifier})'
+
+ def get_absolute_url(self):
+ return reverse('plugins:netbox_routing:eigrpnetwork', args=[self.pk])
+
+
+class EIGRPInterface(PrimaryModel):
+ router = models.ForeignKey(
+ verbose_name=_('Router'),
+ to=EIGRPRouter,
+ on_delete=models.CASCADE,
+ blank=False,
+ null=False
+ )
+ address_family = models.ForeignKey(
+ verbose_name=_('Address Family'),
+ to=EIGRPAddressFamily,
+ on_delete=models.CASCADE,
+ blank=True,
+ null=True
+ )
+ interface = models.ForeignKey(
+ verbose_name=_('Interface'),
+ to='dcim.Interface',
+ related_name='eigrp',
+ on_delete=models.CASCADE,
+ blank=False,
+ null=False
+ )
+ passive = models.BooleanField(
+ verbose_name=_('Passive'),
+ )
+ bfd = models.BooleanField(
+ verbose_name=_('BFD'),
+ )
+ authentication = models.CharField(
+ verbose_name=_('Authentication'),
+ max_length=50,
+ choices=choices.AuthenticationChoices,
+ blank=True,
+ null=True
+ )
+ passphrase = models.CharField(
+ verbose_name=_('Passphrase'),
+ max_length=200,
+ blank=True,
+ null=True
+ )
+
+ class Meta:
+ verbose_name = 'EIGRP Interface'
+ constraints = (
+ models.UniqueConstraint(
+ fields=('router', 'address_family', 'interface',),
+ name='%(app_label)s_%(class)s_unique_interface'
+ ),
+ )
+
+ def __str__(self):
+ if self.address_family:
+ return f'{self.interface.name} ({self.router.identifier} {self.address_family})'
+ return f'{self.interface.name} ({self.router.identifier})'
+
+ def get_absolute_url(self):
+ return reverse('plugins:netbox_routing:eigrpinterface', args=[self.pk])
diff --git a/netbox_routing/models/base.py b/netbox_routing/models/mixins.py
similarity index 100%
rename from netbox_routing/models/base.py
rename to netbox_routing/models/mixins.py
diff --git a/netbox_routing/navigation/__init__.py b/netbox_routing/navigation/__init__.py
index 59ee73e..1bcd4d8 100644
--- a/netbox_routing/navigation/__init__.py
+++ b/netbox_routing/navigation/__init__.py
@@ -3,6 +3,7 @@
from .bgp import MENUITEMS as BGP_MENU
from .objects import MENUITEMS as OBJECT_MENU
from .ospf import MENUITEMS as OSPF_MENU
+from .eigrp import eigrp
from .static import MENUITEMS as STATIC_MENU
@@ -17,6 +18,7 @@
('Static', STATIC_MENU),
# ('BGP', BGP_MENU),
('OSPF', OSPF_MENU),
+ ('EIGRP', eigrp),
),
icon_class='mdi mdi-router'
)
\ No newline at end of file
diff --git a/netbox_routing/navigation/eigrp.py b/netbox_routing/navigation/eigrp.py
new file mode 100644
index 0000000..d69be57
--- /dev/null
+++ b/netbox_routing/navigation/eigrp.py
@@ -0,0 +1,49 @@
+from netbox.choices import ButtonColorChoices
+from netbox.plugins import PluginMenuItem, PluginMenuButton
+
+
+__all__ = (
+ 'eigrp',
+)
+
+
+routers = PluginMenuItem(
+ link='plugins:netbox_routing:eigrprouter_list',
+ link_text='Routers',
+ permissions=['netbox_routing.view_eigrprouter'],
+ buttons=(
+ PluginMenuButton('plugins:netbox_routing:eigrprouter_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN),
+ PluginMenuButton(
+ 'plugins:netbox_routing:eigrprouter_import',
+ 'Import',
+ 'mdi mdi-upload',
+ ButtonColorChoices.CYAN
+ ),
+ )
+)
+address_families = PluginMenuItem(
+ link='plugins:netbox_routing:eigrpaddressfamily_list',
+ link_text='Address Families',
+ permissions=['netbox_routing.view_eigrpaddressfamily'],
+ buttons=(
+ PluginMenuButton('plugins:netbox_routing:eigrpaddressfamily_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN),
+ )
+)
+networks = PluginMenuItem(
+ link='plugins:netbox_routing:eigrpnetwork_list',
+ link_text='Networks',
+ permissions=['netbox_routing.view_eigrpnetwork'],
+ buttons=(
+ PluginMenuButton('plugins:netbox_routing:eigrpnetwork_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN),
+ )
+)
+interfaces = PluginMenuItem(
+ link='plugins:netbox_routing:eigrpinterface_list',
+ link_text='Interfaces',
+ permissions=['netbox_routing.view_eigrpinterface'],
+ buttons=(
+ PluginMenuButton('plugins:netbox_routing:eigrpinterface_add', 'Add', 'mdi mdi-plus', ButtonColorChoices.GREEN),
+ )
+)
+
+eigrp = (routers, address_families, networks, interfaces)
diff --git a/netbox_routing/tables/eigrp.py b/netbox_routing/tables/eigrp.py
new file mode 100644
index 0000000..93e8e85
--- /dev/null
+++ b/netbox_routing/tables/eigrp.py
@@ -0,0 +1,71 @@
+import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+
+from netbox.tables import NetBoxTable
+from netbox_routing.models import EIGRPAddressFamily, EIGRPRouter, EIGRPInterface
+
+
+__all__ = (
+ 'EIGRPAddressFamilyTable',
+ 'EIGRPRouterTable',
+ 'EIGRPNetworkTable',
+ 'EIGRPInterfaceTable',
+)
+
+
+class EIGRPRouterTable(NetBoxTable):
+ class Meta(NetBoxTable.Meta):
+ model = EIGRPRouter
+ fields = ('pk', 'id', 'name', 'mode', 'pid', 'rid', 'device')
+ default_columns = ('pk', 'id', 'name', 'device')
+
+
+class EIGRPAddressFamilyTable(NetBoxTable):
+ router = tables.Column(
+ verbose_name=_('Router'),
+ linkify=True
+ )
+ class Meta(NetBoxTable.Meta):
+ model = EIGRPAddressFamily
+ fields = ('pk', 'id', 'family', 'router')
+ default_columns = ('pk', 'id', 'family', 'router')
+
+
+class EIGRPNetworkTable(NetBoxTable):
+ router = tables.Column(
+ verbose_name=_('Router'),
+ linkify=True
+ )
+ address_family = tables.Column(
+ verbose_name=_('Address Family'),
+ linkify=True
+ )
+ network = tables.Column(
+ verbose_name=_('Network'),
+ linkify=True
+ )
+
+ class Meta(NetBoxTable.Meta):
+ model = EIGRPInterface
+ fields = ('pk', 'id', 'router', 'address_family', 'network')
+ default_columns = ('pk', 'id', 'router', 'address_family', 'network')
+
+
+class EIGRPInterfaceTable(NetBoxTable):
+ router = tables.Column(
+ verbose_name=_('Router'),
+ linkify=True
+ )
+ address_family = tables.Column(
+ verbose_name=_('Address Family'),
+ linkify=True
+ )
+ interface = tables.Column(
+ verbose_name=_('Interface'),
+ linkify=True
+ )
+
+ class Meta(NetBoxTable.Meta):
+ model = EIGRPInterface
+ fields = ('pk', 'id', 'router', 'address_family', 'interface', 'passive', 'bfd', 'authentication', 'passphrase')
+ default_columns = ('pk', 'id', 'router', 'address_family', 'interface')
diff --git a/netbox_routing/templates/netbox_routing/eigrpaddressfamily.html b/netbox_routing/templates/netbox_routing/eigrpaddressfamily.html
new file mode 100644
index 0000000..77c5ebb
--- /dev/null
+++ b/netbox_routing/templates/netbox_routing/eigrpaddressfamily.html
@@ -0,0 +1,55 @@
+{% extends 'generic/object.html' %}
+{% load humanize %}
+{% load helpers %}
+{% load plugins %}
+
+{% block content %}
+
+
+
+
+
+
+
+ Router |
+
+ {{ object.router }}
+ |
+
+
+ VRF |
+
+ {{ object.vrf }}
+ |
+
+
+ Family |
+
+ {{ object.family }}
+ |
+
+
+ Router ID |
+
+ {% if object.rid %}{{ object.rid }}{% else %}{{ object.router.rid}}{% endif %}
+ |
+
+
+
+
+ {% plugin_left_page object %}
+
+
+ {% include 'inc/panels/related_objects.html' %}
+ {% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
+ {% include 'inc/panels/tags.html' %}
+ {% plugin_right_page object %}
+
+
+
+
+ {% plugin_full_width_page object %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/netbox_routing/templates/netbox_routing/eigrpinterface.html b/netbox_routing/templates/netbox_routing/eigrpinterface.html
new file mode 100644
index 0000000..8215fb3
--- /dev/null
+++ b/netbox_routing/templates/netbox_routing/eigrpinterface.html
@@ -0,0 +1,80 @@
+{% extends 'generic/object.html' %}
+{% load humanize %}
+{% load helpers %}
+{% load plugins %}
+
+{% block content %}
+
+
+
+
+
+
+
+ Router |
+
+ {{ object.router|linkify }}
+ |
+
+
+ Address Family |
+
+ {{ object.address_family|linkify }}
+ |
+
+
+ Interface |
+
+ {{ object.interface|linkify }}
+ |
+
+
+
+
+
+
+
+
+
+ Passive |
+
+ {{ object.passive|placeholder }}
+ |
+
+
+ BFD |
+
+ {{ object.bfd|placeholder }}
+ |
+
+
+ Authentication Type |
+
+ {{ object.authentication|placeholder }}
+ |
+
+
+ Passphrase (or keyring) |
+
+ {{ object.passphrase|placeholder }}
+ |
+
+
+
+
+ {% plugin_left_page object %}
+
+
+ {% include 'inc/panels/related_objects.html' %}
+ {% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
+ {% include 'inc/panels/tags.html' %}
+ {% plugin_right_page object %}
+
+
+
+
+ {% plugin_full_width_page object %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/netbox_routing/templates/netbox_routing/eigrpnetwork.html b/netbox_routing/templates/netbox_routing/eigrpnetwork.html
new file mode 100644
index 0000000..bfe58ba
--- /dev/null
+++ b/netbox_routing/templates/netbox_routing/eigrpnetwork.html
@@ -0,0 +1,49 @@
+{% extends 'generic/object.html' %}
+{% load humanize %}
+{% load helpers %}
+{% load plugins %}
+
+{% block content %}
+
+
+
+
+
+
+
+ Router |
+
+ {{ object.router|linkify }}
+ |
+
+
+ Address Family |
+
+ {{ object.address_family|linkify }}
+ |
+
+
+ Network |
+
+ {{ object.network|linkify }}
+ |
+
+
+
+
+ {% plugin_left_page object %}
+
+
+ {% include 'inc/panels/related_objects.html' %}
+ {% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
+ {% include 'inc/panels/tags.html' %}
+ {% plugin_right_page object %}
+
+
+
+
+ {% plugin_full_width_page object %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/netbox_routing/templates/netbox_routing/eigrprouter.html b/netbox_routing/templates/netbox_routing/eigrprouter.html
new file mode 100644
index 0000000..ba285de
--- /dev/null
+++ b/netbox_routing/templates/netbox_routing/eigrprouter.html
@@ -0,0 +1,55 @@
+{% extends 'generic/object.html' %}
+{% load humanize %}
+{% load helpers %}
+{% load plugins %}
+
+{% block content %}
+
+
+
+
+
+
+
+ Device |
+
+ {{ object.device|linkify }}
+ |
+
+
+ Router ID |
+
+ {{ object.rid }}
+ |
+
+
+ Name |
+
+ {{ object.name }}
+ |
+
+
+ Process ID |
+
+ {{ object.process_id }}
+ |
+
+
+
+
+ {% plugin_left_page object %}
+
+
+ {% include 'inc/panels/related_objects.html' %}
+ {% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
+ {% include 'inc/panels/tags.html' %}
+ {% plugin_right_page object %}
+
+
+
+
+ {% plugin_full_width_page object %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/netbox_routing/urls.py b/netbox_routing/urls.py
index dfe26de..d2a095a 100644
--- a/netbox_routing/urls.py
+++ b/netbox_routing/urls.py
@@ -1,11 +1,13 @@
-from django.urls import path
+from django.urls import path, include
from netbox.views.generic import ObjectChangeLogView
+from utilities.urls import get_model_urls
from . import views
from .models import StaticRoute, PrefixList, PrefixListEntry, RouteMap, RouteMapEntry, OSPFInstance, OSPFArea, \
OSPFInterface, BGPRouter, BGPScope, BGPAddressFamily
+
urlpatterns = [
path('routes/static/', views.StaticRouteListView.as_view(), name='staticroute_list'),
path('routes/static/add/', views.StaticRouteEditView.as_view(), name='staticroute_add'),
@@ -50,6 +52,32 @@
path('ospf/interface//delete/', views.OSPFInterfaceDeleteView.as_view(), name='ospfinterface_delete'),
path('ospf/interface//changelog/', ObjectChangeLogView.as_view(), name='ospfinterface_changelog', kwargs={'model': OSPFInterface}),
+ path('eigrp/router/', views.EIGRPRouterListView.as_view(), name='eigrprouter_list'),
+ path('eigrp/router/add/', views.EIGRPRouterEditView.as_view(), name='eigrprouter_add'),
+ path('eigrp/router/edit/', views.EIGRPRouterBulkEditView.as_view(), name='eigrprouter_bulk_edit'),
+ path('eigrp/router/delete/', views.EIGRPRouterBulkDeleteView.as_view(), name='eigrprouter_bulk_delete'),
+ path('eigrp/router/import/', views.EIGRPRouterImportView.as_view(), name='eigrprouter_import'),
+ path('eigrp/router//', include(get_model_urls('netbox_routing', 'eigrprouter'))),
+
+ path('eigrp/address-family/', views.EIGRPAddressFamilyListView.as_view(), name='eigrpaddressfamily_list'),
+ path('eigrp/address-family/add/', views.EIGRPAddressFamilyEditView.as_view(), name='eigrpaddressfamily_add'),
+ path('eigrp/address-family/edit/', views.EIGRPAddressFamilyBulkEditView.as_view(), name='eigrpaddressfamily_bulk_edit'),
+ path('eigrp/address-family/delete/', views.EIGRPAddressFamilyBulkDeleteView.as_view(), name='eigrpaddressfamily_bulk_delete'),
+ path('eigrp/address-family//', include(get_model_urls('netbox_routing', 'eigrpaddressfamily'))),
+
+ path('eigrp/network/', views.EIGRPNetworkListView.as_view(), name='eigrpnetwork_list'),
+ path('eigrp/network/add/', views.EIGRPNetworkEditView.as_view(), name='eigrpnetwork_add'),
+ path('eigrp/network/edit/', views.EIGRPNetworkBulkEditView.as_view(), name='eigrpnetwork_bulk_edit'),
+ path('eigrp/network/delete/', views.EIGRPNetworkBulkDeleteView.as_view(), name='eigrpnetwork_bulk_delete'),
+ path('eigrp/network//', include(get_model_urls('netbox_routing', 'eigrpnetwork'))),
+
+ path('eigrp/interface/', views.EIGRPInterfaceListView.as_view(), name='eigrpinterface_list'),
+ path('eigrp/interface/add/', views.EIGRPInterfaceEditView.as_view(), name='eigrpinterface_add'),
+ path('eigrp/interface/import/', views.EIGRPInterfaceListView.as_view(), name='eigrpinterface_import'),
+ path('eigrp/interface/edit/', views.EIGRPInterfaceBulkEditView.as_view(), name='eigrpinterface_bulk_edit'),
+ path('eigrp/interface/delete/', views.EIGRPInterfaceBulkDeleteView.as_view(), name='eigrpinterface_bulk_delete'),
+ path('eigrp/interface//', include(get_model_urls('netbox_routing', 'eigrpinterface'))),
+
path('bgp/router/', views.BGPRouterListView.as_view(), name='bgprouter_list'),
path('bgp/router/add/', views.BGPRouterEditView.as_view(), name='bgprouter_add'),
path('bgp/router//', views.BGPRouterView.as_view(), name='bgprouter'),
diff --git a/netbox_routing/views/__init__.py b/netbox_routing/views/__init__.py
index 63e6deb..ba0e2a7 100644
--- a/netbox_routing/views/__init__.py
+++ b/netbox_routing/views/__init__.py
@@ -7,6 +7,7 @@
RouteMapEntryBulkDeleteView, PrefixListEntryBulkDeleteView, PrefixListEntryBulkEditView
from .ospf import *
+from .eigrp import *
from .bgp import *
from .core import *
@@ -41,6 +42,38 @@
'OSPFInterfaceEditView',
'OSPFInterfaceDeleteView',
+ # EIGRP
+ 'EIGRPRouterListView',
+ 'EIGRPRouterView',
+ 'EIGRPRouterInterfacesView',
+ 'EIGRPRouterEditView',
+ 'EIGRPRouterImportView',
+ 'EIGRPRouterBulkEditView',
+ 'EIGRPRouterDeleteView',
+ 'EIGRPRouterBulkDeleteView',
+
+ 'EIGRPAddressFamilyListView',
+ 'EIGRPAddressFamilyView',
+ 'EIGRPAddressFamilyInterfacesView',
+ 'EIGRPAddressFamilyEditView',
+ 'EIGRPAddressFamilyBulkEditView',
+ 'EIGRPAddressFamilyDeleteView',
+ 'EIGRPAddressFamilyBulkDeleteView',
+
+ 'EIGRPNetworkListView',
+ 'EIGRPNetworkView',
+ 'EIGRPNetworkEditView',
+ 'EIGRPNetworkBulkEditView',
+ 'EIGRPNetworkDeleteView',
+ 'EIGRPNetworkBulkDeleteView',
+
+ 'EIGRPInterfaceListView',
+ 'EIGRPInterfaceView',
+ 'EIGRPInterfaceEditView',
+ 'EIGRPInterfaceBulkEditView',
+ 'EIGRPInterfaceDeleteView',
+ 'EIGRPInterfaceBulkDeleteView',
+
'BGPRouterView',
'BGPRouterEditView',
diff --git a/netbox_routing/views/eigrp.py b/netbox_routing/views/eigrp.py
new file mode 100644
index 0000000..029d2f1
--- /dev/null
+++ b/netbox_routing/views/eigrp.py
@@ -0,0 +1,309 @@
+from netbox.views.generic import ObjectListView, ObjectEditView, ObjectView, ObjectDeleteView, ObjectChildrenView, \
+ BulkImportView, BulkEditView, BulkDeleteView
+from netbox_routing.filtersets.eigrp import *
+from netbox_routing.forms import *
+from netbox_routing.tables.eigrp import *
+from utilities.views import register_model_view, ViewTab
+
+from netbox_routing.models import *
+
+
+__all__ = (
+ 'EIGRPRouterListView',
+ 'EIGRPRouterView',
+ 'EIGRPRouterAddressFamiliesView',
+ 'EIGRPRouterInterfacesView',
+ 'EIGRPRouterNetworksView',
+ 'EIGRPRouterEditView',
+ 'EIGRPRouterImportView',
+ 'EIGRPRouterBulkEditView',
+ 'EIGRPRouterDeleteView',
+ 'EIGRPRouterBulkDeleteView',
+
+ 'EIGRPAddressFamilyListView',
+ 'EIGRPAddressFamilyView',
+ 'EIGRPAddressFamilyInterfacesView',
+ 'EIGRPAddressFamilyNetworksView',
+ 'EIGRPAddressFamilyEditView',
+ 'EIGRPAddressFamilyBulkEditView',
+ 'EIGRPAddressFamilyDeleteView',
+ 'EIGRPAddressFamilyBulkDeleteView',
+
+ 'EIGRPNetworkListView',
+ 'EIGRPNetworkView',
+ 'EIGRPNetworkEditView',
+ 'EIGRPNetworkBulkEditView',
+ 'EIGRPNetworkDeleteView',
+ 'EIGRPNetworkBulkDeleteView',
+
+ 'EIGRPInterfaceListView',
+ 'EIGRPInterfaceView',
+ 'EIGRPInterfaceEditView',
+ 'EIGRPInterfaceBulkEditView',
+ 'EIGRPInterfaceDeleteView',
+ 'EIGRPInterfaceBulkDeleteView',
+)
+
+
+#
+# Instance
+#
+@register_model_view(EIGRPRouter, name='list')
+class EIGRPRouterListView(ObjectListView):
+ queryset = EIGRPRouter.objects.all()
+ table = EIGRPRouterTable
+ filterset = EIGRPRouterFilterSet
+ filterset_form = EIGRPRouterFilterForm
+
+
+@register_model_view(EIGRPRouter)
+class EIGRPRouterView(ObjectView):
+ queryset = EIGRPRouter.objects.all()
+ template_name = 'netbox_routing/eigrprouter.html'
+
+
+@register_model_view(EIGRPRouter, name='address_families')
+class EIGRPRouterAddressFamiliesView(ObjectChildrenView):
+ queryset = EIGRPRouter.objects.all()
+ child_model = EIGRPAddressFamily
+ table = EIGRPAddressFamilyTable
+ filterset = EIGRPAddressFamilyFilterSet
+ tab = ViewTab(
+ label='Address Families',
+ badge=lambda obj: EIGRPAddressFamily.objects.filter(router=obj).count(),
+ hide_if_empty=False,
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.filter(router=parent)
+
+
+@register_model_view(EIGRPRouter, name='interfaces')
+class EIGRPRouterInterfacesView(ObjectChildrenView):
+ queryset = EIGRPRouter.objects.all()
+ child_model = EIGRPInterface
+ table = EIGRPInterfaceTable
+ filterset = EIGRPInterfaceFilterSet
+ tab = ViewTab(
+ label='Interfaces',
+ badge=lambda obj: EIGRPInterface.objects.filter(router=obj).count(),
+ hide_if_empty=False,
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.filter(router=parent)
+
+
+@register_model_view(EIGRPRouter, name='networks')
+class EIGRPRouterNetworksView(ObjectChildrenView):
+ queryset = EIGRPRouter.objects.all()
+ child_model = EIGRPNetwork
+ table = EIGRPNetworkTable
+ filterset = EIGRPNetworkFilterSet
+ tab = ViewTab(
+ label='Networks',
+ badge=lambda obj: EIGRPNetwork.objects.filter(router=obj).count(),
+ hide_if_empty=False,
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.filter(router=parent)
+
+
+@register_model_view(EIGRPRouter, name='edit')
+class EIGRPRouterEditView(ObjectEditView):
+ queryset = EIGRPRouter.objects.all()
+ form = EIGRPRouterForm
+
+
+@register_model_view(EIGRPRouter, name='bulk_edit')
+class EIGRPRouterBulkEditView(BulkEditView):
+ queryset = EIGRPRouter.objects.all()
+ filterset = EIGRPRouterFilterSet
+ table = EIGRPRouterTable
+ form = EIGRPRouterBulkEditForm
+
+
+@register_model_view(EIGRPRouter, name='delete')
+class EIGRPRouterDeleteView(ObjectDeleteView):
+ queryset = EIGRPRouter.objects.all()
+
+
+@register_model_view(EIGRPRouter, name='bulk_delete')
+class EIGRPRouterBulkDeleteView(BulkDeleteView):
+ queryset = EIGRPRouter.objects.all()
+ filterset = EIGRPRouterFilterSet
+ table = EIGRPRouterTable
+
+
+class EIGRPRouterImportView(BulkImportView):
+ queryset = EIGRPRouter.objects.all()
+ model_form = EIGRPRouterImportForm
+
+#
+# Address Family
+#
+@register_model_view(EIGRPAddressFamily, name='list')
+class EIGRPAddressFamilyListView(ObjectListView):
+ queryset = EIGRPAddressFamily.objects.all()
+ table = EIGRPAddressFamilyTable
+ filterset = EIGRPAddressFamilyFilterSet
+ filterset_form = EIGRPAddressFamilyFilterForm
+
+
+@register_model_view(EIGRPAddressFamily)
+class EIGRPAddressFamilyView(ObjectView):
+ queryset = EIGRPAddressFamily.objects.all()
+ template_name = 'netbox_routing/eigrpaddressfamily.html'
+
+
+@register_model_view(EIGRPAddressFamily, name='interfaces')
+class EIGRPAddressFamilyInterfacesView(ObjectChildrenView):
+ #template_name = 'netbox_routing/object_children.html'
+ queryset = EIGRPAddressFamily.objects.all()
+ child_model = EIGRPInterface
+ table = EIGRPInterfaceTable
+ filterset = EIGRPInterfaceFilterSet
+ tab = ViewTab(
+ label='Interfaces',
+ badge=lambda obj: EIGRPInterface.objects.filter(address_family=obj).count(),
+ )
+
+
+@register_model_view(EIGRPAddressFamily, name='networks')
+class EIGRPAddressFamilyNetworksView(ObjectChildrenView):
+ #template_name = 'netbox_routing/object_children.html'
+ queryset = EIGRPAddressFamily.objects.all()
+ child_model = EIGRPNetwork
+ table = EIGRPNetworkTable
+ filterset = EIGRPNetworkFilterSet
+ tab = ViewTab(
+ label='Interfaces',
+ badge=lambda obj: EIGRPInterface.objects.filter(address_family=obj).count(),
+ )
+
+ def get_children(self, request, parent):
+ return self.child_model.objects.filter(address_famiily=parent)
+
+
+@register_model_view(EIGRPAddressFamily, name='edit')
+class EIGRPAddressFamilyEditView(ObjectEditView):
+ queryset = EIGRPAddressFamily.objects.all()
+ form = EIGRPAddressFamilyForm
+
+
+@register_model_view(EIGRPAddressFamily, name='bulk_edit')
+class EIGRPAddressFamilyBulkEditView(BulkEditView):
+ queryset = EIGRPAddressFamily.objects.all()
+ table = EIGRPAddressFamilyTable
+ filterset = EIGRPAddressFamilyFilterSet
+ form = EIGRPAddressFamilyBulkEditForm
+
+
+@register_model_view(EIGRPAddressFamily, name='delete')
+class EIGRPAddressFamilyDeleteView(ObjectDeleteView):
+ queryset = EIGRPAddressFamily.objects.all()
+
+
+@register_model_view(EIGRPAddressFamily, name='delete')
+class EIGRPAddressFamilyBulkDeleteView(BulkDeleteView):
+ queryset = EIGRPAddressFamily.objects.all()
+ table = EIGRPAddressFamilyTable
+ filterset = EIGRPAddressFamilyFilterSet
+
+
+class EIGRPAddressFamilyImportView(BulkImportView):
+ queryset = EIGRPAddressFamily.objects.all()
+ model_form = EIGRPAddressFamilyImportForm
+
+
+#
+# Network
+#
+@register_model_view(EIGRPNetwork, name='list')
+class EIGRPNetworkListView(ObjectListView):
+ queryset = EIGRPNetwork.objects.all()
+ table = EIGRPNetworkTable
+ filterset = EIGRPNetworkFilterSet
+ filterset_form = EIGRPNetworkFilterForm
+
+
+@register_model_view(EIGRPNetwork)
+class EIGRPNetworkView(ObjectView):
+ queryset = EIGRPNetwork.objects.all()
+ template_name = 'netbox_routing/eigrpnetwork.html'
+
+
+@register_model_view(EIGRPNetwork, name='edit')
+class EIGRPNetworkEditView(ObjectEditView):
+ queryset = EIGRPNetwork.objects.all()
+ form = EIGRPNetworkForm
+
+
+@register_model_view(EIGRPNetwork, name='delete')
+class EIGRPNetworkDeleteView(ObjectDeleteView):
+ queryset = EIGRPNetwork.objects.all()
+
+
+class EIGRPNetworkImportView(BulkImportView):
+ queryset = EIGRPNetwork.objects.all()
+ model_form = EIGRPNetworkImportForm
+
+
+class EIGRPNetworkBulkEditView(BulkEditView):
+ queryset = EIGRPNetwork.objects.all()
+ filterset = EIGRPNetworkFilterSet
+ table = EIGRPNetworkTable
+ form = EIGRPNetworkBulkEditForm
+
+
+class EIGRPNetworkBulkDeleteView(BulkDeleteView):
+ queryset = EIGRPNetwork.objects.all()
+ filterset = EIGRPNetworkFilterSet
+ table = EIGRPNetworkTable
+
+
+#
+# Interface
+#
+@register_model_view(EIGRPInterface, name='list')
+class EIGRPInterfaceListView(ObjectListView):
+ queryset = EIGRPInterface.objects.all()
+ table = EIGRPInterfaceTable
+ filterset = EIGRPInterfaceFilterSet
+ filterset_form = EIGRPInterfaceFilterForm
+
+
+@register_model_view(EIGRPInterface)
+class EIGRPInterfaceView(ObjectView):
+ queryset = EIGRPInterface.objects.all()
+ template_name = 'netbox_routing/eigrpinterface.html'
+
+
+@register_model_view(EIGRPInterface, name='edit')
+class EIGRPInterfaceEditView(ObjectEditView):
+ queryset = EIGRPInterface.objects.all()
+ form = EIGRPInterfaceForm
+
+
+@register_model_view(EIGRPInterface, name='delete')
+class EIGRPInterfaceDeleteView(ObjectDeleteView):
+ queryset = EIGRPInterface.objects.all()
+
+
+class EIGRPInterfaceImportView(BulkImportView):
+ queryset = EIGRPInterface.objects.all()
+ model_form = EIGRPInterfaceImportForm
+
+
+class EIGRPInterfaceBulkEditView(BulkEditView):
+ queryset = EIGRPInterface.objects.all()
+ filterset = EIGRPInterfaceFilterSet
+ table = EIGRPInterfaceTable
+ form = EIGRPInterfaceBulkEditForm
+
+
+class EIGRPInterfaceBulkDeleteView(BulkDeleteView):
+ queryset = EIGRPInterface.objects.all()
+ filterset = EIGRPInterfaceFilterSet
+ table = EIGRPInterfaceTable
diff --git a/pyproject.toml b/pyproject.toml
index 34f0799..47950c6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,7 +17,7 @@ description = "A NetBox Routing Plugin"
readme = "README.md"
requires-python = ">=3.10"
keywords = ["netbox-plugin", ]
-version = "0.2.3"
+version = "0.2.4-alpha1"
license = {file = "LICENSE"}
classifiers = [
"Programming Language :: Python :: 3",