Skip to content

Commit db52990

Browse files
authored
Merge pull request #257 from pheus/housekeeping/47-develop-plugin-tests-models
Fixes #47 [Housekeeping]: Develop Plugin Tests - Models
2 parents cc38ad2 + 68b94ab commit db52990

File tree

9 files changed

+1433
-78
lines changed

9 files changed

+1433
-78
lines changed

netbox_acls/forms/models.py

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@
4343
# Sets a standard help_text value to be used by the various classes for acl index
4444
help_text_acl_rule_index = "Determines the order of the rule in the ACL processing. AKA Sequence Number."
4545

46-
# Sets a standard error message for ACL rules with an action of remark, but no remark set.
47-
error_message_no_remark = "Action is set to remark, you MUST add a remark."
48-
# Sets a standard error message for ACL rules with an action of remark, but no source_prefix is set.
49-
error_message_action_remark_source_prefix_set = "Action is set to remark, Source Prefix CANNOT be set."
50-
# Sets a standard error message for ACL rules with an action not set to remark, but no remark is set.
51-
error_message_remark_without_action_remark = "CANNOT set remark unless action is set to remark."
52-
5346

5447
class AccessListForm(NetBoxModelForm):
5548
"""
@@ -545,35 +538,6 @@ class Meta:
545538
),
546539
}
547540

548-
def clean(self):
549-
"""
550-
Validates form inputs before submitting:
551-
- Check if action set to remark, but no remark set.
552-
- Check if action set to remark, but source_prefix set.
553-
- Check remark set, but action not set to remark.
554-
"""
555-
super().clean()
556-
cleaned_data = self.cleaned_data
557-
error_message = {}
558-
559-
action = cleaned_data.get("action")
560-
remark = cleaned_data.get("remark")
561-
source_prefix = cleaned_data.get("source_prefix")
562-
563-
if action == "remark":
564-
# Check if action set to remark, but no remark set.
565-
if not remark:
566-
error_message["remark"] = [error_message_no_remark]
567-
# Check if action set to remark, but source_prefix set.
568-
if source_prefix:
569-
error_message["source_prefix"] = [error_message_action_remark_source_prefix_set]
570-
# Check remark set, but action not set to remark.
571-
elif remark:
572-
error_message["remark"] = [error_message_remark_without_action_remark]
573-
574-
if error_message:
575-
raise ValidationError(error_message)
576-
577541

578542
class ACLExtendedRuleForm(NetBoxModelForm):
579543
"""
@@ -651,45 +615,3 @@ class Meta:
651615
),
652616
"source_ports": help_text_acl_rule_logic,
653617
}
654-
655-
def clean(self):
656-
"""
657-
Validates form inputs before submitting:
658-
- Check if action set to remark, but no remark set.
659-
- Check if action set to remark, but source_prefix set.
660-
- Check if action set to remark, but source_ports set.
661-
- Check if action set to remark, but destination_prefix set.
662-
- Check if action set to remark, but destination_ports set.
663-
- Check if action set to remark, but protocol set.
664-
- Check remark set, but action not set to remark.
665-
"""
666-
super().clean()
667-
cleaned_data = self.cleaned_data
668-
error_message = {}
669-
670-
action = cleaned_data.get("action")
671-
remark = cleaned_data.get("remark")
672-
source_prefix = cleaned_data.get("source_prefix")
673-
source_ports = cleaned_data.get("source_ports")
674-
destination_prefix = cleaned_data.get("destination_prefix")
675-
destination_ports = cleaned_data.get("destination_ports")
676-
protocol = cleaned_data.get("protocol")
677-
678-
if action == "remark":
679-
if not remark:
680-
error_message["remark"] = [error_message_no_remark]
681-
if source_prefix:
682-
error_message["source_prefix"] = [error_message_action_remark_source_prefix_set]
683-
if source_ports:
684-
error_message["source_ports"] = ["Action is set to remark, Source Ports CANNOT be set."]
685-
if destination_prefix:
686-
error_message["destination_prefix"] = ["Action is set to remark, Destination Prefix CANNOT be set."]
687-
if destination_ports:
688-
error_message["destination_ports"] = ["Action is set to remark, Destination Ports CANNOT be set."]
689-
if protocol:
690-
error_message["protocol"] = ["Action is set to remark, Protocol CANNOT be set."]
691-
elif remark:
692-
error_message["remark"] = [error_message_remark_without_action_remark]
693-
694-
if error_message:
695-
raise ValidationError(error_message)

netbox_acls/models/access_list_rules.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from django.contrib.postgres.fields import ArrayField
6+
from django.core.exceptions import ValidationError
67
from django.db import models
78
from django.urls import reverse
89
from django.utils.translation import gettext_lazy as _
@@ -17,6 +18,29 @@
1718
"ACLExtendedRule",
1819
)
1920

21+
# Error message when the action is 'remark', but no remark is provided.
22+
ERROR_MESSAGE_NO_REMARK = _("When the action is 'remark', a remark is required.")
23+
24+
# Error message when the action is 'remark', but the source_prefix is set.
25+
ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET = _("When the action is 'remark', the Source Prefix must not be set.")
26+
27+
# Error message when the action is 'remark', but the source_ports are set.
28+
ERROR_MESSAGE_ACTION_REMARK_SOURCE_PORTS_SET = _("When the action is 'remark', Source Ports must not be set.")
29+
30+
# Error message when the action is 'remark', but the destination_prefix is set.
31+
ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PREFIX_SET = _(
32+
"When the action is 'remark', the Destination Prefix must not be set."
33+
)
34+
35+
# Error message when the action is 'remark', but the destination_ports are set.
36+
ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PORTS_SET = _("When the action is 'remark', Destination Ports must not be set.")
37+
38+
# Error message when the action is 'remark', but the protocol is set.
39+
ERROR_MESSAGE_ACTION_REMARK_PROTOCOL_SET = _("When the action is 'remark', Protocol must not be set.")
40+
41+
# Error message when a remark is provided, but the action is not set to 'remark'.
42+
ERROR_MESSAGE_REMARK_WITHOUT_ACTION_REMARK = _("A remark cannot be set unless the action is 'remark'.")
43+
2044

2145
class ACLRule(NetBoxModel):
2246
"""
@@ -111,6 +135,31 @@ class Meta(ACLRule.Meta):
111135
verbose_name = _("ACL Standard Rule")
112136
verbose_name_plural = _("ACL Standard Rules")
113137

138+
def clean(self):
139+
"""
140+
Validate the ACL Standard Rule inputs.
141+
142+
If the action is 'remark', then the remark field must be provided (non-empty),
143+
and the source_prefix field must be empty.
144+
Conversely, if the remark field is provided, the action must be set to 'remark'.
145+
"""
146+
147+
super().clean()
148+
errors = {}
149+
150+
# Validate that only the remark field is filled
151+
if self.action == ACLRuleActionChoices.ACTION_REMARK:
152+
if not self.remark:
153+
errors["remark"] = ERROR_MESSAGE_NO_REMARK
154+
if self.source_prefix:
155+
errors["source_prefix"] = ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET
156+
# Validate that the action is "remark", when the remark field is provided
157+
elif self.remark:
158+
errors["remark"] = ERROR_MESSAGE_REMARK_WITHOUT_ACTION_REMARK
159+
160+
if errors:
161+
raise ValidationError(errors)
162+
114163

115164
class ACLExtendedRule(ACLRule):
116165
"""
@@ -173,5 +222,43 @@ class Meta(ACLRule.Meta):
173222
verbose_name = _("ACL Extended Rule")
174223
verbose_name_plural = _("ACL Extended Rules")
175224

225+
def clean(self):
226+
"""
227+
Validate the ACL Extended Rule inputs.
228+
229+
When the action is 'remark', the remark field must be provided (non-empty),
230+
and the following fields must be empty:
231+
- source_prefix
232+
- source_ports
233+
- destination_prefix
234+
- destination_ports
235+
- protocol
236+
237+
Conversely, if a remark is provided, the action must be set to 'remark'.
238+
"""
239+
super().clean()
240+
errors = {}
241+
242+
# Validate that only the remark field is filled
243+
if self.action == ACLRuleActionChoices.ACTION_REMARK:
244+
if not self.remark:
245+
errors["remark"] = ERROR_MESSAGE_NO_REMARK
246+
if self.source_prefix:
247+
errors["source_prefix"] = ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET
248+
if self.source_ports:
249+
errors["source_ports"] = ERROR_MESSAGE_ACTION_REMARK_SOURCE_PORTS_SET
250+
if self.destination_prefix:
251+
errors["destination_prefix"] = ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PREFIX_SET
252+
if self.destination_ports:
253+
errors["destination_ports"] = ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PORTS_SET
254+
if self.protocol:
255+
errors["protocol"] = ERROR_MESSAGE_ACTION_REMARK_PROTOCOL_SET
256+
# Validate that the action is "remark", when the remark field is provided
257+
elif self.remark:
258+
errors["remark"] = ERROR_MESSAGE_REMARK_WITHOUT_ACTION_REMARK
259+
260+
if errors:
261+
raise ValidationError(errors)
262+
176263
def get_protocol_color(self):
177264
return ACLProtocolChoices.colors.get(self.protocol)

netbox_acls/models/access_lists.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from dcim.models import Device, Interface, VirtualChassis
66
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
77
from django.contrib.contenttypes.models import ContentType
8+
from django.core.exceptions import ValidationError
89
from django.core.validators import RegexValidator
910
from django.db import models
1011
from django.urls import reverse
@@ -155,6 +156,18 @@ def get_absolute_url(self):
155156
args=[self.pk],
156157
)
157158

159+
def save(self, *args, **kwargs):
160+
"""Saves the current instance to the database."""
161+
# Ensure the assigned interface's host matches the host assigned to the access list.
162+
if self.assigned_object.parent_object != self.access_list.assigned_object:
163+
raise ValidationError(
164+
{
165+
"assigned_object": "The assigned object must be the same as the device assigned to it."
166+
}
167+
)
168+
169+
super().save(*args, **kwargs)
170+
158171
def get_direction_color(self):
159172
return ACLAssignmentDirectionChoices.colors.get(self.direction)
160173

netbox_acls/tests/models/__init__.py

Whitespace-only changes.

netbox_acls/tests/models/base.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from dcim.models import (
2+
Device,
3+
DeviceRole,
4+
DeviceType,
5+
Manufacturer,
6+
Site,
7+
VirtualChassis,
8+
)
9+
from django.test import TestCase
10+
from ipam.models import Prefix
11+
from virtualization.models import Cluster, ClusterType, VirtualMachine
12+
13+
14+
class BaseTestCase(TestCase):
15+
"""
16+
Base test case for netbox_acls models.
17+
"""
18+
19+
@classmethod
20+
def setUpTestData(cls):
21+
"""
22+
Create base data to test using including
23+
- 1 of each of the following: test site, manufacturer, device type
24+
device role, cluster type, cluster, virtual chassis, and
25+
virtual machine
26+
- 2 of each Device, prefix
27+
"""
28+
29+
# Sites
30+
site = Site.objects.create(
31+
name="Site 1",
32+
slug="site-1",
33+
)
34+
35+
# Device Types
36+
manufacturer = Manufacturer.objects.create(
37+
name="Manufacturer 1",
38+
slug="manufacturer-1",
39+
)
40+
device_type = DeviceType.objects.create(
41+
manufacturer=manufacturer,
42+
model="Device Type 1",
43+
)
44+
45+
# Device Roles
46+
device_role = DeviceRole.objects.create(
47+
name="Device Role 1",
48+
slug="device-role-1",
49+
)
50+
51+
# Devices
52+
cls.device1 = Device.objects.create(
53+
name="Device 1",
54+
site=site,
55+
device_type=device_type,
56+
role=device_role,
57+
)
58+
cls.device2 = Device.objects.create(
59+
name="Device 2",
60+
site=site,
61+
device_type=device_type,
62+
role=device_role,
63+
)
64+
65+
# Virtual Chassis
66+
cls.virtual_chassis1 = VirtualChassis.objects.create(
67+
name="Virtual Chassis 1",
68+
)
69+
70+
# Virtual Chassis Members
71+
cls.virtual_chassis_member1 = Device.objects.create(
72+
name="VC Device",
73+
site=site,
74+
device_type=device_type,
75+
role=device_role,
76+
virtual_chassis=cls.virtual_chassis1,
77+
vc_position=1,
78+
)
79+
80+
# Virtualization Cluster Type
81+
cluster_type = ClusterType.objects.create(
82+
name="Cluster Type 1",
83+
)
84+
85+
# Virtualization Cluster
86+
cluster = Cluster.objects.create(
87+
name="Cluster 1",
88+
type=cluster_type,
89+
)
90+
91+
# Virtualization Cluster Member
92+
cls.cluster_member1 = Device.objects.create(
93+
name="Cluster Device",
94+
site=site,
95+
device_type=device_type,
96+
role=device_role,
97+
)
98+
99+
# Virtual Machine
100+
cls.virtual_machine1 = VirtualMachine.objects.create(
101+
name="VirtualMachine 1",
102+
status="active",
103+
cluster=cluster,
104+
)
105+
cls.virtual_machine2 = VirtualMachine.objects.create(
106+
name="VirtualMachine 2",
107+
status="active",
108+
cluster=cluster,
109+
)
110+
111+
# Prefix
112+
cls.prefix1 = Prefix.objects.create(
113+
prefix="10.1.0.0/16",
114+
)
115+
cls.prefix2 = Prefix.objects.create(
116+
prefix="10.2.0.0/16",
117+
)

0 commit comments

Comments
 (0)