Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions netbox_routing/forms/static.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
from django.utils.translation import gettext as _

from dcim.models import Device
from ipam.models import VRF
from netbox.forms import NetBoxModelForm
from netbox_routing.models import StaticRoute
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, CommentField
from utilities.forms.rendering import FieldSet


class StaticRouteForm(NetBoxModelForm):
devices = DynamicModelMultipleChoiceField(
queryset=Device.objects.all()
queryset=Device.objects.all(),
label=_('Devices'),
)
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label=_('VRF'),
)
comments = CommentField()

fieldsets = (
FieldSet(
'devices',
'vrf',
),
FieldSet(
'prefix',
'next_hop',
'metric',
name=_('Route'),
),
FieldSet(
'name',
'description',
'tag',
'permanent',
name=_('Metadata'),
),
)

class Meta:
model = StaticRoute
fields = (
Expand Down
23 changes: 16 additions & 7 deletions netbox_routing/models/static.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import CheckConstraint, Q
from django.urls import reverse
from django.utils.translation import gettext as _

from ipam.fields import IPNetworkField
from netbox.models import PrimaryModel
Expand All @@ -23,27 +25,27 @@ class StaticRoute(PrimaryModel):
related_name='staticroutes',
blank=True,
null=True,
verbose_name='VRF'
)
prefix = IPNetworkField(help_text='IPv4 or IPv6 network with mask')
next_hop = IPAddressField()
prefix = IPNetworkField(
help_text=_('IPv4 or IPv6 network with mask'),
)
next_hop = IPAddressField(
verbose_name=_('Next Hop'),
)
name = models.CharField(
max_length=50,
verbose_name='Name',
blank=True,
null=True,
help_text='Optional name for this static route'
)
metric = models.PositiveSmallIntegerField(
verbose_name='Metric',
blank=True,
default=1,
)
permanent = models.BooleanField(default=False, blank=True, null=True,)

tag = models.IntegerField(
verbose_name='Tag',
help_text='Optional tag for this static route',
blank=True,
null=True
)
Expand All @@ -63,7 +65,7 @@ class Meta:
models.UniqueConstraint(
'vrf', 'prefix', 'next_hop',
name='%(app_label)s_%(class)s_unique_vrf_prefix_nexthop',
violation_error_message="VRF, Prefix and Next Hop must be unique."
violation_error_message=_('VRF, Prefix and Next Hop must be unique.'),
),
)

Expand All @@ -74,3 +76,10 @@ def __str__(self):

def get_absolute_url(self):
return reverse('plugins:netbox_routing:staticroute', args=[self.pk])

def clean(self):
super().clean()

# IPv4 and IPv6 cannot be mixed.
if self.prefix.version != self.next_hop.version:
raise ValidationError(_('The IP version must be the same for the prefix and next hop.'))
2 changes: 1 addition & 1 deletion netbox_routing/navigation/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

static = PluginMenuItem(
link='plugins:netbox_routing:staticroute_list',
link_text='Static Route',
link_text='Static Routes',
permissions=['netbox_routing.view_staticroute'],
buttons=(
PluginMenuButton('plugins:netbox_routing:staticroute_add', 'Add', 'mdi mdi-plus', ColorChoices.GREEN),
Expand Down
26 changes: 22 additions & 4 deletions netbox_routing/tests/static/test_models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
# from django.core.exceptions import ValidationError
from django.core.exceptions import ValidationError
from django.test import TestCase
from netaddr import IPAddress, IPNetwork

# from utilities.testing import create_test_device
from utilities.testing import create_test_device

# from netbox_routing.models import *
from netbox_routing.models.static import StaticRoute

__all__ = (
'StaticRouteTestCase',
)


class StaticRouteTestCase(TestCase):
pass
@classmethod
def setUpTestData(cls):
super().setUpTestData()

cls.device = create_test_device(name='Device 1')

def test_clean_ip_versions(self):
# pass: both IPv4
route1 = StaticRoute(prefix=IPNetwork('0.0.0.0/0'), next_hop=IPAddress('1.2.3.4'))
route1.clean()

# pass: both IPv6
route2 = StaticRoute(prefix=IPNetwork('::/0'), next_hop=IPAddress('fe80::1'))
route2.clean()

# fail: mixed
route3 = StaticRoute(prefix=IPNetwork('0.0.0.0/0'), next_hop=IPAddress('fe80::1'))
self.assertRaises(ValidationError, route3.clean)
2 changes: 2 additions & 0 deletions netbox_routing/views/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class StaticRouteDevicesView(ObjectChildrenView):
tab = ViewTab(
label='Assigned Devices',
badge=lambda obj: Device.objects.filter(static_routes=obj).count(),
permission='netbox_routing.view_staticroute',
hide_if_empty=True,
)

def get_children(self, request, parent):
Expand Down