Skip to content

Fixes #47 [Housekeeping]: Develop Plugin Tests - Models #257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
78 changes: 0 additions & 78 deletions netbox_acls/forms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@
# Sets a standard help_text value to be used by the various classes for acl index
help_text_acl_rule_index = "Determines the order of the rule in the ACL processing. AKA Sequence Number."

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


class AccessListForm(NetBoxModelForm):
"""
Expand Down Expand Up @@ -545,35 +538,6 @@ class Meta:
),
}

def clean(self):
"""
Validates form inputs before submitting:
- Check if action set to remark, but no remark set.
- Check if action set to remark, but source_prefix set.
- Check remark set, but action not set to remark.
"""
super().clean()
cleaned_data = self.cleaned_data
error_message = {}

action = cleaned_data.get("action")
remark = cleaned_data.get("remark")
source_prefix = cleaned_data.get("source_prefix")

if action == "remark":
# Check if action set to remark, but no remark set.
if not remark:
error_message["remark"] = [error_message_no_remark]
# Check if action set to remark, but source_prefix set.
if source_prefix:
error_message["source_prefix"] = [error_message_action_remark_source_prefix_set]
# Check remark set, but action not set to remark.
elif remark:
error_message["remark"] = [error_message_remark_without_action_remark]

if error_message:
raise ValidationError(error_message)


class ACLExtendedRuleForm(NetBoxModelForm):
"""
Expand Down Expand Up @@ -651,45 +615,3 @@ class Meta:
),
"source_ports": help_text_acl_rule_logic,
}

def clean(self):
"""
Validates form inputs before submitting:
- Check if action set to remark, but no remark set.
- Check if action set to remark, but source_prefix set.
- Check if action set to remark, but source_ports set.
- Check if action set to remark, but destination_prefix set.
- Check if action set to remark, but destination_ports set.
- Check if action set to remark, but protocol set.
- Check remark set, but action not set to remark.
"""
super().clean()
cleaned_data = self.cleaned_data
error_message = {}

action = cleaned_data.get("action")
remark = cleaned_data.get("remark")
source_prefix = cleaned_data.get("source_prefix")
source_ports = cleaned_data.get("source_ports")
destination_prefix = cleaned_data.get("destination_prefix")
destination_ports = cleaned_data.get("destination_ports")
protocol = cleaned_data.get("protocol")

if action == "remark":
if not remark:
error_message["remark"] = [error_message_no_remark]
if source_prefix:
error_message["source_prefix"] = [error_message_action_remark_source_prefix_set]
if source_ports:
error_message["source_ports"] = ["Action is set to remark, Source Ports CANNOT be set."]
if destination_prefix:
error_message["destination_prefix"] = ["Action is set to remark, Destination Prefix CANNOT be set."]
if destination_ports:
error_message["destination_ports"] = ["Action is set to remark, Destination Ports CANNOT be set."]
if protocol:
error_message["protocol"] = ["Action is set to remark, Protocol CANNOT be set."]
elif remark:
error_message["remark"] = [error_message_remark_without_action_remark]

if error_message:
raise ValidationError(error_message)
88 changes: 88 additions & 0 deletions netbox_acls/models/access_list_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

from django.apps import apps
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from netbox.models import NetBoxModel

from ..choices import ACLProtocolChoices, ACLRuleActionChoices, ACLTypeChoices
Expand All @@ -17,6 +19,29 @@
"ACLExtendedRule",
)

# Error message when the action is 'remark', but no remark is provided.
ERROR_MESSAGE_NO_REMARK = _("When the action is 'remark', a remark is required.")

# Error message when the action is 'remark', but the source_prefix is set.
ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET = _("When the action is 'remark', the Source Prefix must not be set.")

# Error message when the action is 'remark', but the source_ports are set.
ERROR_MESSAGE_ACTION_REMARK_SOURCE_PORTS_SET = _("When the action is 'remark', Source Ports must not be set.")

# Error message when the action is 'remark', but the destination_prefix is set.
ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PREFIX_SET = _(
"When the action is 'remark', the Destination Prefix must not be set."
)

# Error message when the action is 'remark', but the destination_ports are set.
ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PORTS_SET = _("When the action is 'remark', Destination Ports must not be set.")

# Error message when the action is 'remark', but the protocol is set.
ERROR_MESSAGE_ACTION_REMARK_PROTOCOL_SET = _("When the action is 'remark', Protocol must not be set.")

# Error message when a remark is provided, but the action is not set to 'remark'.
ERROR_MESSAGE_REMARK_WITHOUT_ACTION_REMARK = _("A remark cannot be set unless the action is 'remark'.")


class ACLRule(NetBoxModel):
"""
Expand Down Expand Up @@ -112,6 +137,31 @@ class Meta(ACLRule.Meta):
verbose_name = "ACL Standard Rule"
verbose_name_plural = "ACL Standard Rules"

def clean(self):
"""
Validate the ACL Standard Rule inputs.

If the action is 'remark', then the remark field must be provided (non-empty),
and the source_prefix field must be empty.
Conversely, if the remark field is provided, the action must be set to 'remark'.
"""

super().clean()
errors = {}

# Validate that only the remark field is filled
if self.action == ACLRuleActionChoices.ACTION_REMARK:
if not self.remark:
errors["remark"] = ERROR_MESSAGE_NO_REMARK
if self.source_prefix:
errors["source_prefix"] = ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET
# Validate that the action is "remark", when the remark field is provided
elif self.remark:
errors["remark"] = ERROR_MESSAGE_REMARK_WITHOUT_ACTION_REMARK

if errors:
raise ValidationError(errors)


class ACLExtendedRule(ACLRule):
"""
Expand Down Expand Up @@ -176,3 +226,41 @@ class Meta(ACLRule.Meta):

verbose_name = "ACL Extended Rule"
verbose_name_plural = "ACL Extended Rules"

def clean(self):
"""
Validate the ACL Extended Rule inputs.

When the action is 'remark', the remark field must be provided (non-empty),
and the following fields must be empty:
- source_prefix
- source_ports
- destination_prefix
- destination_ports
- protocol

Conversely, if a remark is provided, the action must be set to 'remark'.
"""
super().clean()
errors = {}

# Validate that only the remark field is filled
if self.action == ACLRuleActionChoices.ACTION_REMARK:
if not self.remark:
errors["remark"] = ERROR_MESSAGE_NO_REMARK
if self.source_prefix:
errors["source_prefix"] = ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET
if self.source_ports:
errors["source_ports"] = ERROR_MESSAGE_ACTION_REMARK_SOURCE_PORTS_SET
if self.destination_prefix:
errors["destination_prefix"] = ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PREFIX_SET
if self.destination_ports:
errors["destination_ports"] = ERROR_MESSAGE_ACTION_REMARK_DESTINATION_PORTS_SET
if self.protocol:
errors["protocol"] = ERROR_MESSAGE_ACTION_REMARK_PROTOCOL_SET
# Validate that the action is "remark", when the remark field is provided
elif self.remark:
errors["remark"] = ERROR_MESSAGE_REMARK_WITHOUT_ACTION_REMARK

if errors:
raise ValidationError(errors)
13 changes: 13 additions & 0 deletions netbox_acls/models/access_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from dcim.models import Device, Interface, VirtualChassis
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models
from django.urls import reverse
Expand Down Expand Up @@ -149,6 +150,18 @@ def get_absolute_url(self):
args=[self.pk],
)

def save(self, *args, **kwargs):
"""Saves the current instance to the database."""
# Ensure the assigned interface's host matches the host assigned to the access list.
if self.assigned_object.parent_object != self.access_list.assigned_object:
raise ValidationError(
{
"assigned_object": "The assigned object must be the same as the device assigned to it."
}
)

super().save(*args, **kwargs)

def get_direction_color(self):
return ACLAssignmentDirectionChoices.colors.get(self.direction)

Expand Down
Empty file.
117 changes: 117 additions & 0 deletions netbox_acls/tests/models/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from dcim.models import (
Device,
DeviceRole,
DeviceType,
Manufacturer,
Site,
VirtualChassis,
)
from django.test import TestCase
from ipam.models import Prefix
from virtualization.models import Cluster, ClusterType, VirtualMachine


class BaseTestCase(TestCase):
"""
Base test case for netbox_acls models.
"""

@classmethod
def setUpTestData(cls):
"""
Create base data to test using including
- 1 of each of the following: test site, manufacturer, device type
device role, cluster type, cluster, virtual chassis, and
virtual machine
- 2 of each Device, prefix
"""

# Sites
site = Site.objects.create(
name="Site 1",
slug="site-1",
)

# Device Types
manufacturer = Manufacturer.objects.create(
name="Manufacturer 1",
slug="manufacturer-1",
)
device_type = DeviceType.objects.create(
manufacturer=manufacturer,
model="Device Type 1",
)

# Device Roles
device_role = DeviceRole.objects.create(
name="Device Role 1",
slug="device-role-1",
)

# Devices
cls.device1 = Device.objects.create(
name="Device 1",
site=site,
device_type=device_type,
role=device_role,
)
cls.device2 = Device.objects.create(
name="Device 2",
site=site,
device_type=device_type,
role=device_role,
)

# Virtual Chassis
cls.virtual_chassis1 = VirtualChassis.objects.create(
name="Virtual Chassis 1",
)

# Virtual Chassis Members
cls.virtual_chassis_member1 = Device.objects.create(
name="VC Device",
site=site,
device_type=device_type,
role=device_role,
virtual_chassis=cls.virtual_chassis1,
vc_position=1,
)

# Virtualization Cluster Type
cluster_type = ClusterType.objects.create(
name="Cluster Type 1",
)

# Virtualization Cluster
cluster = Cluster.objects.create(
name="Cluster 1",
type=cluster_type,
)

# Virtualization Cluster Member
cls.cluster_member1 = Device.objects.create(
name="Cluster Device",
site=site,
device_type=device_type,
role=device_role,
)

# Virtual Machine
cls.virtual_machine1 = VirtualMachine.objects.create(
name="VirtualMachine 1",
status="active",
cluster=cluster,
)
cls.virtual_machine2 = VirtualMachine.objects.create(
name="VirtualMachine 2",
status="active",
cluster=cluster,
)

# Prefix
cls.prefix1 = Prefix.objects.create(
prefix="10.1.0.0/16",
)
cls.prefix2 = Prefix.objects.create(
prefix="10.2.0.0/16",
)
Loading