Skip to content

Commit

Permalink
Merge pull request #200 from netbox-community/dev
Browse files Browse the repository at this point in the history
Prepare 4.0 #197
  • Loading branch information
cruse1977 authored Jun 7, 2024
2 parents da0fffc + 79be0d6 commit 4586ceb
Show file tree
Hide file tree
Showing 16 changed files with 227 additions and 101 deletions.
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Thank you for sharing your work and for opening a PR.
(!) IMPORTANT (!):
First make sure that you point your PR to the `devel` branch!
First make sure that you point your PR to the `dev` branch!
Now please read the comments carefully and try to provide information
on all relevant titles.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
fetch-depth: 0

- name: Lint Code Base
uses: github/super-linter/slim@v5
uses: github/super-linter/slim@v6
env:
DEFAULT_BRANCH: dev
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NETBOX_VARIANT=v3.7
ARG NETBOX_VARIANT=v4.0

FROM netboxcommunity/netbox:${NETBOX_VARIANT}

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Each Plugin Version listed below has been tested with its corresponding NetBox V

| NetBox Version | Plugin Version |
|:--------------:|:--------------:|
| >= 4.0.2 | 1.6.0 |
| 3.7 | 1.5.0 |
| 3.6 | 1.4.0 |
| 3.5 | 1.3.0 |
Expand Down Expand Up @@ -79,6 +80,12 @@ PLUGINS_CONFIG = {
}
```

To add the required `netbox-acls` tables to your NetBox database, run the `migrate` manager subcommand in the NetBox virtual environment:
```
cd /opt/netbox
sudo ./venv/bin/python3 netbox/manage.py migrate
```

## Developing

### VSCode + Docker + Dev Containers
Expand Down
1 change: 1 addition & 0 deletions netbox_acls/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @ryanmerolle @abhi1693 @cruse1977 @natm
6 changes: 3 additions & 3 deletions netbox_acls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Define the NetBox Plugin
"""

from extras.plugins import PluginConfig
from netbox.plugins import PluginConfig

from .version import __version__

Expand All @@ -17,8 +17,8 @@ class NetBoxACLsConfig(PluginConfig):
version = __version__
description = "Manage simple ACLs in NetBox"
base_url = "access-lists"
min_version = "3.7.0"
max_version = "3.7.99"
min_version = "4.0.2"
max_version = "4.0.99"


config = NetBoxACLsConfig
83 changes: 46 additions & 37 deletions netbox_acls/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class Meta:
"last_updated",
"rule_count",
)
brief_fields = ("id", "url", "name", "display")

@extend_schema_field(serializers.DictField())
def get_assigned_object(self, obj):
Expand Down Expand Up @@ -139,6 +140,7 @@ class Meta:
"created",
"last_updated",
)
brief_fields = ("id", "url", "access_list")

@extend_schema_field(serializers.DictField())
def get_assigned_object(self, obj):
Expand Down Expand Up @@ -212,6 +214,7 @@ class Meta:
"last_updated",
"source_prefix",
)
brief_fields = ("id", "url", "display")

def validate(self, data):
"""
Expand All @@ -221,14 +224,17 @@ def validate(self, data):
"""
error_message = {}

# Check if action set to remark, but no remark set.
if data.get("action") == "remark" and data.get("remark") is None:
error_message["remark"] = [error_message_no_remark]
# Check if action set to remark, but source_prefix set.
if data.get("source_prefix"):
error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set,
]
if data.get("action") == "remark":
# Check if action set to remark, but no remark set.
if data.get("remark") is None:
error_message["remark"] = [
error_message_no_remark,
]
# Check if action set to remark, but source_prefix set.
if data.get("source_prefix"):
error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set,
]

if error_message:
raise serializers.ValidationError(error_message)
Expand Down Expand Up @@ -281,7 +287,7 @@ class Meta:
"protocol",
"remark",
)

brief_fields = ("id", "url", "display")
def validate(self, data):
"""
Validate the ACLExtendedRule django model's inputs before allowing it to update the instance:
Expand All @@ -295,34 +301,37 @@ def validate(self, data):
"""
error_message = {}

# Check if action set to remark, but no remark set.
if data.get("action") == "remark" and data.get("remark") is None:
error_message["remark"] = [error_message_no_remark]
# Check if action set to remark, but source_prefix set.
if data.get("source_prefix"):
error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set,
]
# Check if action set to remark, but source_ports set.
if data.get("source_ports"):
error_message["source_ports"] = [
"Action is set to remark, Source Ports CANNOT be set.",
]
# Check if action set to remark, but destination_prefix set.
if data.get("destination_prefix"):
error_message["destination_prefix"] = [
"Action is set to remark, Destination Prefix CANNOT be set.",
]
# Check if action set to remark, but destination_ports set.
if data.get("destination_ports"):
error_message["destination_ports"] = [
"Action is set to remark, Destination Ports CANNOT be set.",
]
# Check if action set to remark, but protocol set.
if data.get("protocol"):
error_message["protocol"] = [
"Action is set to remark, Protocol CANNOT be set.",
]
if data.get("action") == "remark":
# Check if action set to remark, but no remark set.
if data.get("remark") is None:
error_message["remark"] = [
error_message_no_remark,
]
# Check if action set to remark, but source_prefix set.
if data.get("source_prefix"):
error_message["source_prefix"] = [
error_message_action_remark_source_prefix_set,
]
# Check if action set to remark, but source_ports set.
if data.get("source_ports"):
error_message["source_ports"] = [
"Action is set to remark, Source Ports CANNOT be set.",
]
# Check if action set to remark, but destination_prefix set.
if data.get("destination_prefix"):
error_message["destination_prefix"] = [
"Action is set to remark, Destination Prefix CANNOT be set.",
]
# Check if action set to remark, but destination_ports set.
if data.get("destination_ports"):
error_message["destination_ports"] = [
"Action is set to remark, Destination Ports CANNOT be set.",
]
# Check if action set to remark, but protocol set.
if data.get("protocol"):
error_message["protocol"] = [
"Action is set to remark, Protocol CANNOT be set.",
]

if error_message:
raise serializers.ValidationError(error_message)
Expand Down
35 changes: 31 additions & 4 deletions netbox_acls/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
import django_filters
from dcim.models import Device, Interface, VirtualChassis
from django.db.models import Q
from netbox.filtersets import NetBoxModelFilterSet
from virtualization.models import VirtualMachine, VMInterface

Expand Down Expand Up @@ -80,7 +81,16 @@ def search(self, queryset, name, value):
"""
Override the default search behavior for the django model.
"""
return queryset.filter(description__icontains=value)
query = (
Q(name__icontains=value)
| Q(device__name__icontains=value)
| Q(virtual_chassis__name__icontains=value)
| Q(virtual_machine__name__icontains=value)
| Q(type__icontains=value)
| Q(default_action__icontains=value)
| Q(comments__icontains=value)
)
return queryset.filter(query)


class ACLInterfaceAssignmentFilterSet(NetBoxModelFilterSet):
Expand Down Expand Up @@ -131,7 +141,13 @@ def search(self, queryset, name, value):
"""
Override the default search behavior for the django model.
"""
return queryset.filter(description__icontains=value)
query = (
Q(access_list__name__icontains=value)
| Q(direction__icontains=value)
| Q(interface__name__icontains=value)
| Q(vminterface__name__icontains=value)
)
return queryset.filter(query)


class ACLStandardRuleFilterSet(NetBoxModelFilterSet):
Expand All @@ -151,7 +167,12 @@ def search(self, queryset, name, value):
"""
Override the default search behavior for the django model.
"""
return queryset.filter(description__icontains=value)
query = (
Q(access_list__name__icontains=value)
| Q(index__icontains=value)
| Q(action__icontains=value)
)
return queryset.filter(query)


class ACLExtendedRuleFilterSet(NetBoxModelFilterSet):
Expand All @@ -171,4 +192,10 @@ def search(self, queryset, name, value):
"""
Override the default search behavior for the django model.
"""
return queryset.filter(description__icontains=value)
query = (
Q(access_list__name__icontains=value)
| Q(index__icontains=value)
| Q(action__icontains=value)
| Q(protocol__icontains=value)
)
return queryset.filter(query)
7 changes: 7 additions & 0 deletions netbox_acls/graphql/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
from .schema import *
from .types import *

schema = [
schema.NetBoxACLSAccessListQuery,
schema.NetBoxACLSStandardRuleQuery,
schema.NetBoxACLSACLExtendedRuleQuery
]

30 changes: 30 additions & 0 deletions netbox_acls/graphql/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import strawberry_django
from .. import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin

__all__ = (
'AccessListFilter',
'ACLInterfaceAssignmentFilter',
'ACLExtendedRuleFilter',
'ACLStandardRuleFilter',
)

@strawberry_django.filter(models.AccessList, lookups=True)
@autotype_decorator(filtersets.AccessListFilterSet)
class AccessListFilter(BaseFilterMixin):
pass

@strawberry_django.filter(models.ACLStandardRule, lookups=True)
@autotype_decorator(filtersets.ACLStandardRuleFilterSet)
class ACLStandardRuleFilter(BaseFilterMixin):
pass

@strawberry_django.filter(models.ACLExtendedRule, lookups=True)
@autotype_decorator(filtersets.ACLExtendedRuleFilterSet)
class ACLExtendedRuleFilter(BaseFilterMixin):
pass

@strawberry_django.filter(models.ACLInterfaceAssignment, lookups=True)
@autotype_decorator(filtersets.ACLInterfaceAssignmentFilterSet)
class ACLInterfaceAssignmentFilter(BaseFilterMixin):
pass
34 changes: 22 additions & 12 deletions netbox_acls/graphql/schema.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
from graphene import ObjectType
from netbox.graphql.fields import ObjectField, ObjectListField

import strawberry
import strawberry_django
from .types import *
from ..models import *


class Query(ObjectType):
@strawberry.type
class NetBoxACLSAccessListQuery:
"""
Defines the queries available to this plugin via the graphql api.
"""
@strawberry.field
def access_list(self, id: int) -> AccessListType:
return AccessList.objects.get(pk=id)
access_list_list: list[AccessListType] = strawberry_django.field()

@strawberry.type
class NetBoxACLSACLExtendedRuleQuery:
@strawberry.field
def acl_extended_rule(self, id: int) -> ACLExtendedRuleType:
return ACLExtendedRule.objects.get(pk=id)
acl_extended_rule_list: list[ACLExtendedRuleType] = strawberry_django.field()

access_list = ObjectField(AccessListType)
access_list_list = ObjectListField(AccessListType)

acl_extended_rule = ObjectField(ACLExtendedRuleType)
acl_extended_rule_list = ObjectListField(ACLExtendedRuleType)

acl_standard_rule = ObjectField(ACLStandardRuleType)
acl_standard_rule_list = ObjectListField(ACLStandardRuleType)
@strawberry.type
class NetBoxACLSStandardRuleQuery:
@strawberry.field
def acl_standard_rule(self, id: int) -> ACLStandardRuleType:
return ACLStandardRule.objects.get(pk=id)
acl_standard_rule_list: list[ACLStandardRuleType] = strawberry_django.field()


schema = Query
Loading

0 comments on commit 4586ceb

Please sign in to comment.