From b105a3fba8a69ba9937fe899a6913cf251a8869d Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:14:41 +0100 Subject: [PATCH 01/44] update serializers and add filtersets on object rules views --- netbox_bgp/api/serializers.py | 16 +++------------- .../templates/netbox_bgp/communitylist.html | 2 +- netbox_bgp/views.py | 6 +++--- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index cc846bb..2d03c8f 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -1,7 +1,7 @@ from rest_framework.serializers import HyperlinkedIdentityField, ValidationError from rest_framework.relations import PrimaryKeyRelatedField -from netbox.api.fields import ChoiceField +from netbox.api.fields import ChoiceField, SerializedPKRelatedField from netbox.api.serializers.nested import WritableNestedSerializer from netbox.api.serializers import NetBoxModelSerializer @@ -20,20 +20,10 @@ from netbox_bgp.choices import CommunityStatusChoices, SessionStatusChoices -class SerializedPKRelatedField(PrimaryKeyRelatedField): - def __init__(self, serializer, **kwargs): - self.serializer = serializer - self.pk_field = kwargs.pop('pk_field', None) - super().__init__(**kwargs) - - def to_representation(self, value): - return self.serializer(value, context={'request': self.context['request']}).data - - class RoutingPolicySerializer(NetBoxModelSerializer): class Meta: model = RoutingPolicy - fields = ['id', 'name', 'description', 'tags', 'custom_fields', 'comments'] + fields = ['id', 'name', 'display', 'description', 'tags', 'custom_fields', 'comments'] class NestedRoutingPolicySerializer(WritableNestedSerializer): @@ -77,7 +67,7 @@ class BGPPeerGroupSerializer(NetBoxModelSerializer): class Meta: model = BGPPeerGroup - fields = ['id', 'display', 'name', 'description', 'import_policies', 'export_policies', 'comments'] + fields = ['id', 'display', 'name', 'description', 'import_policies', 'export_policies', 'comments', 'tags', 'custom_fields' ] class NestedBGPPeerGroupSerializer(WritableNestedSerializer): diff --git a/netbox_bgp/templates/netbox_bgp/communitylist.html b/netbox_bgp/templates/netbox_bgp/communitylist.html index 27bd89a..20a0dee 100644 --- a/netbox_bgp/templates/netbox_bgp/communitylist.html +++ b/netbox_bgp/templates/netbox_bgp/communitylist.html @@ -47,7 +47,7 @@
name | +Name | {{ object.name }} |
Family | +{{ object.family }} | +|
Description | {{ object.description|placeholder }} | From cc3619bbd498356308f5b3f2c7226060204ae668 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:41:07 +0000 Subject: [PATCH 16/44] redraft graphql --- netbox_bgp/graphql.py | 52 ------------ netbox_bgp/graphql/__init__.py | 3 + netbox_bgp/graphql/filters.py | 93 +++++++++++++++++++++ netbox_bgp/graphql/schema.py | 74 +++++++++++++++++ netbox_bgp/graphql/types.py | 144 +++++++++++++++++++++++++++++++++ 5 files changed, 314 insertions(+), 52 deletions(-) delete mode 100755 netbox_bgp/graphql.py create mode 100644 netbox_bgp/graphql/__init__.py create mode 100644 netbox_bgp/graphql/filters.py create mode 100644 netbox_bgp/graphql/schema.py create mode 100644 netbox_bgp/graphql/types.py diff --git a/netbox_bgp/graphql.py b/netbox_bgp/graphql.py deleted file mode 100755 index f048b99..0000000 --- a/netbox_bgp/graphql.py +++ /dev/null @@ -1,52 +0,0 @@ -from graphene import ObjectType, Field - -from netbox.graphql.scalars import BigInt -from netbox.graphql.types import NetBoxObjectType -from netbox.graphql.fields import ObjectField, ObjectListField - -from . import filtersets, models - - -class CommunityType(NetBoxObjectType): - class Meta: - model = models.Community - fields = '__all__' - filterset_class = filtersets.CommunityFilterSet - - -class BgpSessionType(NetBoxObjectType): - class Meta: - model = models.BGPSession - fields = '__all__' - filterset_class = filtersets.BGPSessionFilterSet - - -class PeerGroupType(NetBoxObjectType): - class Meta: - model = models.BGPPeerGroup - fields = '__all__' - filterset_class = filtersets.BGPPeerGroupFilterSet - - -class RoutingPolicyType(NetBoxObjectType): - class Meta: - model = models.RoutingPolicy - fields = '__all__' - filterset_class = filtersets.RoutingPolicyFilterSet - - -class BGPQuery(ObjectType): - community = ObjectField(CommunityType) - community_list = ObjectListField(CommunityType) - - bgp_session = ObjectField(BgpSessionType) - bgp_session_list = ObjectListField(BgpSessionType) - - peer_group = ObjectField(PeerGroupType) - peer_group_list = ObjectListField(PeerGroupType) - - routing_policy = ObjectField(RoutingPolicyType) - routing_policy_list = ObjectListField(RoutingPolicyType) - - -schema = BGPQuery diff --git a/netbox_bgp/graphql/__init__.py b/netbox_bgp/graphql/__init__.py new file mode 100644 index 0000000..1b80483 --- /dev/null +++ b/netbox_bgp/graphql/__init__.py @@ -0,0 +1,3 @@ +from .schema import (NetBoxBGPQuery) + +schema = [NetBoxBGPQuery] \ No newline at end of file diff --git a/netbox_bgp/graphql/filters.py b/netbox_bgp/graphql/filters.py new file mode 100644 index 0000000..d842450 --- /dev/null +++ b/netbox_bgp/graphql/filters.py @@ -0,0 +1,93 @@ +import strawberry_django +from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin + +from netbox_bgp.models import ( + Community, + BGPSession, + RoutingPolicy, + BGPPeerGroup, + RoutingPolicyRule, + PrefixList, + PrefixListRule, + CommunityList, + CommunityListRule, +) + +from netbox_bgp.filtersets import ( + CommunityFilterSet, + BGPSessionFilterSet, + BGPPeerGroupFilterSet, + RoutingPolicyFilterSet, + RoutingPolicyRuleFilterSet, + PrefixListFilterSet, + PrefixListRuleFilterSet, + CommunityListFilterSet, + CommunityListRuleFilterSet, +) + + +__all__ = ( + "CommunityFilter", + "BGPSessionFilter", + "BGPPeerGroupFilter", + "RoutingPolicyFilter", + "RoutingPolicyRuleFilter", + "PrefixListFilter", + "PrefixListRuleFilter", + "CommunityListFilter", + "CommunityListRuleFilter", +) + + +@strawberry_django.filter(Community, lookups=True) +@autotype_decorator(CommunityFilterSet) +class CommunityFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(BGPSession, lookups=True) +@autotype_decorator(BGPSessionFilterSet) +class BGPSessionFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(BGPPeerGroup, lookups=True) +@autotype_decorator(BGPPeerGroupFilterSet) +class BGPPeerGroupFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(RoutingPolicy, lookups=True) +@autotype_decorator(RoutingPolicyFilterSet) +class RoutingPolicyFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(RoutingPolicyRule, lookups=True) +@autotype_decorator(RoutingPolicyRuleFilterSet) +class RoutingPolicyRuleFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(PrefixList, lookups=True) +@autotype_decorator(PrefixListFilterSet) +class PrefixListFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(PrefixListRule, lookups=True) +@autotype_decorator(PrefixListRuleFilterSet) +class PrefixListRuleFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(CommunityList, lookups=True) +@autotype_decorator(CommunityListFilterSet) +class CommunityListFilter(BaseFilterMixin): + pass + + +@strawberry_django.filter(CommunityListRule, lookups=True) +@autotype_decorator(CommunityListRuleFilterSet) +class CommunityListRuleFilter(BaseFilterMixin): + pass diff --git a/netbox_bgp/graphql/schema.py b/netbox_bgp/graphql/schema.py new file mode 100644 index 0000000..d9439cc --- /dev/null +++ b/netbox_bgp/graphql/schema.py @@ -0,0 +1,74 @@ +from typing import List + +import strawberry +import strawberry_django + +from netbox_bgp.models import ( + Community, + BGPSession, + RoutingPolicy, + BGPPeerGroup, + RoutingPolicyRule, + PrefixList, + PrefixListRule, + CommunityList, + CommunityListRule, +) +from .types import * + + +@strawberry.type +class NetBoxBGPQuery: + @strawberry.field + def community(self, id: int) -> CommunityType: + return Community.objects.get(pk=id) + + community_list: List[CommunityType] = strawberry_django.field() + + @strawberry.field + def bgp_session(self, id: int) -> BGPSessionType: + return BGPSession.objects.get(pk=id) + + bgp_session_list: List[BGPSessionType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> BGPPeerGroupType: + return BGPPeerGroup.objects.get(pk=id) + + bgp_peer_group_list: List[BGPPeerGroupType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> RoutingPolicyType: + return RoutingPolicy.objects.get(pk=id) + + routing_policies_list: List[RoutingPolicyType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> RoutingPolicyRuleType: + return RoutingPolicyRule.objects.get(pk=id) + + routing_policies_rules_list: List[RoutingPolicyRuleType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> PrefixListType: + return PrefixList.objects.get(pk=id) + + prefixlist_list: List[PrefixListType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> PrefixListRuleType: + return PrefixListRule.objects.get(pk=id) + + prefixlist_rules_list: List[PrefixListRuleType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> CommunityListType: + return CommunityList.objects.get(pk=id) + + communitylist_list: List[CommunityListType] = strawberry_django.field() + + @strawberry.field + def bgp_peer_group(self, id: int) -> CommunityListRuleType: + return CommunityListRule.objects.get(pk=id) + + communitylist_rules_list: List[CommunityListRuleType] = strawberry_django.field() diff --git a/netbox_bgp/graphql/types.py b/netbox_bgp/graphql/types.py new file mode 100644 index 0000000..02230b1 --- /dev/null +++ b/netbox_bgp/graphql/types.py @@ -0,0 +1,144 @@ +from typing import Annotated, List + +import strawberry +import strawberry_django + + +from netbox.graphql.types import NetBoxObjectType +from netbox.graphql.scalars import BigInt + +from netbox_bgp.models import ( + Community, + BGPSession, + RoutingPolicy, + BGPPeerGroup, + RoutingPolicyRule, + PrefixList, + PrefixListRule, + CommunityList, + CommunityListRule, +) +from .filters import * + + +@strawberry_django.type(Community, fields="__all__", filters=CommunityFilter) +class CommunityType(NetBoxObjectType): + site: Annotated["SiteType", strawberry.lazy("dcim.graphql.types")] | None + tenant: Annotated["TenantType", strawberry.lazy("tenancy.graphql.types")] | None + status: str + # role = models.ForeignKey( + description: str + + +@strawberry_django.type(BGPSession, fields="__all__", filters=BGPSessionFilter) +class BGPSessionType(NetBoxObjectType): + name: str + site: Annotated["SiteType", strawberry.lazy("dcim.graphql.types")] | None + tenant: Annotated["TenantType", strawberry.lazy("tenancy.graphql.types")] | None + device: Annotated["DeviceType", strawberry.lazy("dcim.graphql.types")] + local_address: Annotated["IPAddressType", strawberry.lazy("ipam.graphql.types")] + remote_address: Annotated["IPAddressType", strawberry.lazy("ipam.graphql.types")] + local_as: Annotated["ASNType", strawberry.lazy("ipam.graphql.types")] + remote_as: Annotated["ASNType", strawberry.lazy("ipam.graphql.types")] + status: str + description: str + peer_group: ( + Annotated["BGPPeerGroupType", strawberry.lazy("netbox_bgp.graphql.types")] + | None + ) + import_policies: List[ + Annotated["RoutingPolicyType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + export_policies: List[ + Annotated["RoutingPolicyType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + prefix_list_in: List[ + Annotated["PrefixListType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + prefix_list_out: List[ + Annotated["PrefixListType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + + +@strawberry_django.type(BGPPeerGroup, fields="__all__", filters=BGPPeerGroupFilter) +class BGPPeerGroupType(NetBoxObjectType): + name: str + description: str + import_policies: List[ + Annotated["RoutingPolicyType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + export_policies: List[ + Annotated["RoutingPolicyType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + + +@strawberry_django.type(RoutingPolicy, fields="__all__", filters=RoutingPolicyFilter) +class RoutingPolicyType(NetBoxObjectType): + name: str + description: str + + +@strawberry_django.type( + RoutingPolicyRule, fields="__all__", filters=RoutingPolicyRuleFilter +) +class RoutingPolicyRuleType(NetBoxObjectType): + routing_policy: Annotated[ + "RoutingPolicyType", strawberry.lazy("netbox_bgp.graphql.types") + ] + index: BigInt + action: str + description: str + continue_entry: BigInt + match_community: List[ + Annotated["CommunityType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + match_community_list: List[ + Annotated["CommunityListType", strawberry.lazy("netbox_bgp.graphql.types")] + ] + match_ip_address: List[ + Annotated["PrefixType", strawberry.lazy("ipam.graphql.types")] + ] + match_ipv6_address: List[ + Annotated["PrefixType", strawberry.lazy("ipam.graphql.types")] + ] + # match_custom = models.JSONField( + # set_actions = models.JSONField( + + +@strawberry_django.type(PrefixList, fields="__all__", filters=PrefixListFilter) +class PrefixListType(NetBoxObjectType): + name: str + description: str + family: str + + +@strawberry_django.type(PrefixListRule, fields="__all__", filters=PrefixListRuleFilter) +class PrefixListRuleType(NetBoxObjectType): + prefix_list: Annotated[ + "PrefixListType", strawberry.lazy("netbox_bgp.graphql.types") + ] + index: BigInt + action: str + prefix: Annotated["PrefixType", strawberry.lazy("ipam.graphql.types")] + prefix_custom: str + ge: BigInt + le: BigInt + description: str + + +@strawberry_django.type(CommunityList, fields="__all__", filters=CommunityListFilter) +class CommunityListType(NetBoxObjectType): + name: str + description: str + + +@strawberry_django.type( + CommunityListRule, fields="__all__", filters=CommunityListRuleFilter +) +class CommunityListRuleType(NetBoxObjectType): + community_list: Annotated[ + "CommunityListType", strawberry.lazy("netbox_bgp.graphql.types") + ] + action: str + community: Annotated["CommunityType", strawberry.lazy("netbox_bgp.graphql.types")] + description: str From 9e4e2615b7ef3945d590d9a3517fa1c4d51fb1fb Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:38:05 +0000 Subject: [PATCH 17/44] filter prefix list family for routing policy rules fix #187 --- netbox_bgp/forms.py | 510 ++++++++++++++++----------------- netbox_bgp/graphql/__init__.py | 4 +- netbox_bgp/graphql/schema.py | 12 +- netbox_bgp/graphql/types.py | 16 +- 4 files changed, 278 insertions(+), 264 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index c8a8b90..d8be76b 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -1,6 +1,10 @@ from django import forms from django.conf import settings -from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, ValidationError +from django.core.exceptions import ( + MultipleObjectsReturned, + ObjectDoesNotExist, + ValidationError, +) from django.utils.translation import gettext as _ from tenancy.models import Tenant @@ -8,17 +12,31 @@ from ipam.models import IPAddress, Prefix, ASN from ipam.formfields import IPNetworkFormField from utilities.forms.fields import ( - DynamicModelChoiceField, CSVModelChoiceField, + DynamicModelChoiceField, + CSVModelChoiceField, DynamicModelMultipleChoiceField, - TagFilterField, CSVChoiceField, CommentField + TagFilterField, + CSVChoiceField, + CommentField, ) from utilities.forms.widgets import APISelect, APISelectMultiple -from netbox.forms import NetBoxModelForm, NetBoxModelBulkEditForm, NetBoxModelFilterSetForm, NetBoxModelImportForm +from netbox.forms import ( + NetBoxModelForm, + NetBoxModelBulkEditForm, + NetBoxModelFilterSetForm, + NetBoxModelImportForm, +) from .models import ( - Community, BGPSession, RoutingPolicy, BGPPeerGroup, - RoutingPolicyRule, PrefixList, PrefixListRule, - CommunityList, CommunityListRule + Community, + BGPSession, + RoutingPolicy, + BGPPeerGroup, + RoutingPolicyRule, + PrefixList, + PrefixListRule, + CommunityList, + CommunityListRule, ) from .choices import SessionStatusChoices, CommunityStatusChoices @@ -29,36 +47,22 @@ class CommunityForm(NetBoxModelForm): required=False, choices=CommunityStatusChoices, ) - tenant = DynamicModelChoiceField( - queryset=Tenant.objects.all(), - required=False - ) + tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) comments = CommentField() class Meta: model = Community - fields = [ - 'value', 'description', 'status', 'tenant', 'tags', 'comments' - ] + fields = ["value", "description", "status", "tenant", "tags", "comments"] class CommunityFilterForm(NetBoxModelFilterSetForm): - q = forms.CharField( - required=False, - label='Search' - ) - tenant = DynamicModelChoiceField( - queryset=Tenant.objects.all(), - required=False - ) + q = forms.CharField(required=False, label="Search") + tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) status = forms.MultipleChoiceField( choices=CommunityStatusChoices, required=False, ) - site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False - ) + site = DynamicModelChoiceField(queryset=Site.objects.all(), required=False) tag = TagFilterField(Community) @@ -67,17 +71,10 @@ class CommunityFilterForm(NetBoxModelFilterSetForm): class CommunityBulkEditForm(NetBoxModelBulkEditForm): pk = forms.ModelMultipleChoiceField( - queryset=Community.objects.all(), - widget=forms.MultipleHiddenInput - ) - tenant = DynamicModelChoiceField( - queryset=Tenant.objects.all(), - required=False - ) - description = forms.CharField( - max_length=200, - required=False + queryset=Community.objects.all(), widget=forms.MultipleHiddenInput ) + tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) + description = forms.CharField(max_length=200, required=False) status = forms.ChoiceField( required=False, choices=CommunityStatusChoices, @@ -85,7 +82,8 @@ class CommunityBulkEditForm(NetBoxModelBulkEditForm): model = Community nullable_fields = [ - 'tenant', 'description', + "tenant", + "description", ] @@ -93,26 +91,22 @@ class CommunityImportForm(NetBoxModelImportForm): tenant = CSVModelChoiceField( queryset=Tenant.objects.all(), required=False, - to_field_name='name', - help_text=_('Assigned tenant') + to_field_name="name", + help_text=_("Assigned tenant"), ) status = CSVChoiceField( - choices=CommunityStatusChoices, - help_text=_('Operational status') - ) + choices=CommunityStatusChoices, help_text=_("Operational status") + ) class Meta: model = Community - fields = ('value', 'description', 'tags') + fields = ("value", "description", "tags") class CommunityListFilterForm(NetBoxModelFilterSetForm): model = CommunityList - q = forms.CharField( - required=False, - label='Search' - ) + q = forms.CharField(required=False, label="Search") tag = TagFilterField(model) @@ -123,63 +117,40 @@ class CommunityListForm(NetBoxModelForm): class Meta: model = CommunityList - fields = ['name', 'description', 'tags', 'comments'] + fields = ["name", "description", "tags", "comments"] class CommunityListRuleForm(NetBoxModelForm): community = DynamicModelChoiceField( queryset=Community.objects.all(), required=False, - help_text='Community', + help_text="Community", ) comments = CommentField() class Meta: model = CommunityListRule - fields = [ - 'community_list', - 'action', 'community', - 'tags', 'comments' - ] + fields = ["community_list", "action", "community", "tags", "comments"] class BGPSessionForm(NetBoxModelForm): - name = forms.CharField( - max_length=64, - required=True - ) - site = DynamicModelChoiceField( - queryset=Site.objects.all(), - required=False - ) + name = forms.CharField(max_length=64, required=True) + site = DynamicModelChoiceField(queryset=Site.objects.all(), required=False) device = DynamicModelChoiceField( - queryset=Device.objects.all(), - required=False, - query_params={ - 'site_id': '$site' - } - ) - tenant = DynamicModelChoiceField( - queryset=Tenant.objects.all(), - required=False + queryset=Device.objects.all(), required=False, query_params={"site_id": "$site"} ) + tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) local_as = DynamicModelChoiceField( queryset=ASN.objects.all(), - query_params={ - 'site_id': '$site' - }, - label=_('Local AS') + query_params={"site_id": "$site"}, + label=_("Local AS"), ) remote_as = DynamicModelChoiceField( - queryset=ASN.objects.all(), - label=_('Remote AS') + queryset=ASN.objects.all(), label=_("Remote AS") ) local_address = DynamicModelChoiceField( - queryset=IPAddress.objects.all(), - query_params={ - 'device_id': '$device' - } + queryset=IPAddress.objects.all(), query_params={"device_id": "$device"} ) remote_address = DynamicModelChoiceField( queryset=IPAddress.objects.all(), @@ -188,55 +159,84 @@ class BGPSessionForm(NetBoxModelForm): queryset=BGPPeerGroup.objects.all(), required=False, widget=APISelect( - api_url='/api/plugins/bgp/peer-group/', - ) + api_url="/api/plugins/bgp/peer-group/", + ), ) import_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) export_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) prefix_list_in = DynamicModelChoiceField( queryset=PrefixList.objects.all(), required=False, widget=APISelect( - api_url='/api/plugins/bgp/prefix-list/', - ) + api_url="/api/plugins/bgp/prefix-list/", + ), ) prefix_list_out = DynamicModelChoiceField( queryset=PrefixList.objects.all(), required=False, widget=APISelect( - api_url='/api/plugins/bgp/prefix-list/', - ) - ) + api_url="/api/plugins/bgp/prefix-list/", + ), + ) comments = CommentField() class Meta: model = BGPSession fields = [ - 'name', 'site', 'device', - 'local_as', 'remote_as', 'local_address', 'remote_address', - 'description', 'status', 'peer_group', 'tenant', 'tags', 'import_policies', 'export_policies', - 'prefix_list_in', 'prefix_list_out','comments' + "name", + "site", + "device", + "local_as", + "remote_as", + "local_address", + "remote_address", + "description", + "status", + "peer_group", + "tenant", + "tags", + "import_policies", + "export_policies", + "prefix_list_in", + "prefix_list_out", + "comments", ] fieldsets = ( - ('Session', ('name', 'site', 'device', 'description', 'status', 'peer_group', 'tenant', 'tags')), - ('Remote', ('remote_as', 'remote_address')), - ('Local', ('local_as', 'local_address')), - ('Policies', ('import_policies', 'export_policies', 'prefix_list_in', 'prefix_list_out')) + ( + "Session", + ( + "name", + "site", + "device", + "description", + "status", + "peer_group", + "tenant", + "tags", + ), + ), + ("Remote", ("remote_as", "remote_address")), + ("Local", ("local_as", "local_address")), + ( + "Policies", + ( + "import_policies", + "export_policies", + "prefix_list_in", + "prefix_list_out", + ), + ), ) widgets = { - 'status': forms.Select(), + "status": forms.Select(), } @@ -245,120 +245,116 @@ class BGPSessionAddForm(BGPSessionForm): def clean_remote_address(self): try: - ip = IPAddress.objects.get(address=str(self.cleaned_data['remote_address'])) + ip = IPAddress.objects.get(address=str(self.cleaned_data["remote_address"])) except MultipleObjectsReturned: - ip = IPAddress.objects.filter(address=str(self.cleaned_data['remote_address'])).first() + ip = IPAddress.objects.filter( + address=str(self.cleaned_data["remote_address"]) + ).first() except ObjectDoesNotExist: - ip = IPAddress.objects.create(address=str(self.cleaned_data['remote_address'])) - self.cleaned_data['remote_address'] = ip - return self.cleaned_data['remote_address'] + ip = IPAddress.objects.create( + address=str(self.cleaned_data["remote_address"]) + ) + self.cleaned_data["remote_address"] = ip + return self.cleaned_data["remote_address"] class BGPSessionImportForm(NetBoxModelImportForm): site = CSVModelChoiceField( - label=_('Site'), + label=_("Site"), required=False, queryset=Site.objects.all(), - to_field_name='name', - help_text=_('Assigned site') + to_field_name="name", + help_text=_("Assigned site"), ) tenant = CSVModelChoiceField( queryset=Tenant.objects.all(), required=False, - to_field_name='name', - help_text=_('Assigned tenant') + to_field_name="name", + help_text=_("Assigned tenant"), ) device = CSVModelChoiceField( queryset=Device.objects.all(), - to_field_name='name', - help_text=_('Assigned device') + to_field_name="name", + help_text=_("Assigned device"), ) status = CSVChoiceField( - choices=SessionStatusChoices, - required=False, - help_text=_('Operational status') - ) + choices=SessionStatusChoices, required=False, help_text=_("Operational status") + ) local_address = CSVModelChoiceField( queryset=IPAddress.objects.all(), - to_field_name='address', - help_text=_('Local IP Address'), + to_field_name="address", + help_text=_("Local IP Address"), ) remote_address = CSVModelChoiceField( queryset=IPAddress.objects.all(), - to_field_name='address', - help_text=_('Remote IP Address'), + to_field_name="address", + help_text=_("Remote IP Address"), ) local_as = CSVModelChoiceField( queryset=ASN.objects.all(), - to_field_name='asn', - help_text=_('Local ASN'), + to_field_name="asn", + help_text=_("Local ASN"), ) remote_as = CSVModelChoiceField( queryset=ASN.objects.all(), - to_field_name='asn', - help_text=_('Remote ASN'), + to_field_name="asn", + help_text=_("Remote ASN"), ) peer_group = CSVModelChoiceField( queryset=BGPPeerGroup.objects.all(), required=False, - to_field_name='name', - help_text=_('Peer Group'), + to_field_name="name", + help_text=_("Peer Group"), ) prefix_list_in = CSVModelChoiceField( queryset=PrefixList.objects.all(), required=False, - to_field_name='name', - help_text=_('Prefix list In'), - ) + to_field_name="name", + help_text=_("Prefix list In"), + ) prefix_list_out = CSVModelChoiceField( queryset=PrefixList.objects.all(), required=False, - to_field_name='name', - help_text=_('Prefix List Out'), + to_field_name="name", + help_text=_("Prefix List Out"), ) class Meta: model = BGPSession fields = [ - 'name', 'device', 'site', 'description', 'tenant', 'status', 'peer_group', - 'local_address', 'remote_address', 'local_as', 'remote_as', 'tags', - 'prefix_list_in', 'prefix_list_out' + "name", + "device", + "site", + "description", + "tenant", + "status", + "peer_group", + "local_address", + "remote_address", + "local_as", + "remote_as", + "tags", + "prefix_list_in", + "prefix_list_out", ] class BGPSessionFilterForm(NetBoxModelFilterSetForm): model = BGPSession - q = forms.CharField( - required=False, - label='Search' - ) + q = forms.CharField(required=False, label="Search") remote_as_id = DynamicModelMultipleChoiceField( - queryset=ASN.objects.all(), - required=False, - label=_('Remote AS') + queryset=ASN.objects.all(), required=False, label=_("Remote AS") ) local_as_id = DynamicModelMultipleChoiceField( - queryset=ASN.objects.all(), - required=False, - label=_('Local AS') - ) - by_local_address = forms.CharField( - required=False, - label='Local Address' - ) - by_remote_address = forms.CharField( - required=False, - label='Remote Address' + queryset=ASN.objects.all(), required=False, label=_("Local AS") ) + by_local_address = forms.CharField(required=False, label="Local Address") + by_remote_address = forms.CharField(required=False, label="Remote Address") device_id = DynamicModelMultipleChoiceField( - queryset=Device.objects.all(), - required=False, - label=_('Device') + queryset=Device.objects.all(), required=False, label=_("Device") ) site_id = DynamicModelMultipleChoiceField( - queryset=Site.objects.all(), - required=False, - label=_('Site') + queryset=Site.objects.all(), required=False, label=_("Site") ) status = forms.MultipleChoiceField( choices=SessionStatusChoices, @@ -367,116 +363,94 @@ class BGPSessionFilterForm(NetBoxModelFilterSetForm): peer_group = DynamicModelMultipleChoiceField( queryset=BGPPeerGroup.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/peer-group/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/peer-group/"), ) import_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) export_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) prefix_list_in = DynamicModelMultipleChoiceField( queryset=PrefixList.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/prefix-list/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/prefix-list/"), ) prefix_list_out = DynamicModelMultipleChoiceField( queryset=PrefixList.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/prefix-list/' - ) - ) - tenant = DynamicModelChoiceField( - queryset=Tenant.objects.all(), - required=False + widget=APISelectMultiple(api_url="/api/plugins/bgp/prefix-list/"), ) + tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False) tag = TagFilterField(model) class BGPSessionBulkEditForm(NetBoxModelBulkEditForm): device = DynamicModelChoiceField( - label=_('Device'), + label=_("Device"), queryset=Device.objects.all(), required=False, ) site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False + label=_("Site"), queryset=Site.objects.all(), required=False ) status = forms.ChoiceField( - label=_('Status'), + label=_("Status"), required=False, choices=SessionStatusChoices, ) description = forms.CharField( - label=_('Description'), - max_length=200, - required=False + label=_("Description"), max_length=200, required=False ) tenant = DynamicModelChoiceField( - label=_('Tenant'), - queryset=Tenant.objects.all(), - required=False - ) - local_as = DynamicModelChoiceField( - queryset=ASN.objects.all(), - required=False - ) - remote_as = DynamicModelChoiceField( - queryset=ASN.objects.all(), - required=False + label=_("Tenant"), queryset=Tenant.objects.all(), required=False ) + local_as = DynamicModelChoiceField(queryset=ASN.objects.all(), required=False) + remote_as = DynamicModelChoiceField(queryset=ASN.objects.all(), required=False) peer_group = DynamicModelChoiceField( queryset=BGPPeerGroup.objects.all(), required=False, widget=APISelect( - api_url='/api/plugins/bgp/peer-group/', - ) + api_url="/api/plugins/bgp/peer-group/", + ), ) import_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) export_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) model = BGPSession fieldsets = ( - (('Session'), ('device', 'site', 'description', 'status', 'tenant', 'peer_group')), - (('AS'), ('local_as', 'remote_as')), - (('Policies'), ('import_policies', 'export_policies')), + ( + ("Session"), + ("device", "site", "description", "status", "tenant", "peer_group"), + ), + (("AS"), ("local_as", "remote_as")), + (("Policies"), ("import_policies", "export_policies")), ) - nullable_fields = ['tenant', 'description', 'peer_group', 'import_policies', 'export_policies'] + nullable_fields = [ + "tenant", + "description", + "peer_group", + "import_policies", + "export_policies", + ] + class RoutingPolicyFilterForm(NetBoxModelFilterSetForm): model = RoutingPolicy - q = forms.CharField( - required=False, - label='Search' - ) + q = forms.CharField(required=False, label="Search") tag = TagFilterField(model) @@ -487,15 +461,12 @@ class RoutingPolicyForm(NetBoxModelForm): class Meta: model = RoutingPolicy - fields = ['name', 'description', 'tags', 'comments'] + fields = ["name", "description", "tags", "comments"] class BGPPeerGroupFilterForm(NetBoxModelFilterSetForm): model = BGPPeerGroup - q = forms.CharField( - required=False, - label='Search' - ) + q = forms.CharField(required=False, label="Search") tag = TagFilterField(model) @@ -504,29 +475,32 @@ class BGPPeerGroupForm(NetBoxModelForm): import_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) export_policies = DynamicModelMultipleChoiceField( queryset=RoutingPolicy.objects.all(), required=False, - widget=APISelectMultiple( - api_url='/api/plugins/bgp/routing-policy/' - ) + widget=APISelectMultiple(api_url="/api/plugins/bgp/routing-policy/"), ) comments = CommentField() class Meta: model = BGPPeerGroup - fields = ['name', 'description', 'import_policies', 'export_policies', 'tags', 'comments'] + fields = [ + "name", + "description", + "import_policies", + "export_policies", + "tags", + "comments", + ] class RoutingPolicyRuleForm(NetBoxModelForm): continue_entry = forms.IntegerField( required=False, - label='Continue', - help_text='Null for disable, 0 to next entry, or any sequence number' + label="Continue", + help_text="Null for disable, 0 to next entry, or any sequence number", ) match_community = DynamicModelMultipleChoiceField( queryset=Community.objects.all(), @@ -535,44 +509,60 @@ class RoutingPolicyRuleForm(NetBoxModelForm): match_community_list = DynamicModelMultipleChoiceField( queryset=CommunityList.objects.all(), required=False, - ) - match_ip_address = DynamicModelMultipleChoiceField( - queryset=PrefixList.objects.all(), - required=False, - label='Match IP address Prefix lists', ) - match_ipv6_address = DynamicModelMultipleChoiceField( - queryset=PrefixList.objects.all(), - required=False, - label='Match IPv6 address Prefix lists', + match_ip_address = forms.MultipleChoiceField( + choices=[], required=False, label="Match IPv4 address Prefix lists" ) + + match_ipv6_address = forms.MultipleChoiceField( + choices=[], required=False, label="Match IPv6 address Prefix lists" + ) + match_custom = forms.JSONField( - label='Custom Match', + label="Custom Match", help_text='Any custom match statements, e.g., {"ip nexthop": "1.1.1.1"}', required=False, ) set_actions = forms.JSONField( - label='Set statements', + label="Set statements", help_text='Set statements, e.g., {"as-path prepend": [12345,12345]}', - required=False + required=False, ) comments = CommentField() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + instance = kwargs.get("instance", {}) + if instance: + _prefix_v4 = PrefixList.objects.filter(family="ipv4") + _prefix_v6 = PrefixList.objects.filter(family="ipv6") + prefix_v4 = list(set([(prefix.id, prefix.name) for prefix in _prefix_v4])) + prefix_v6 = list(set([(prefix.id, prefix.name) for prefix in _prefix_v6])) + self.fields["match_ip_address"].choices = prefix_v4 + self.fields["match_ipv6_address"].choices = prefix_v6 + class Meta: model = RoutingPolicyRule fields = [ - 'routing_policy', 'index', 'action', 'continue_entry', 'match_community', - 'match_community_list','match_ip_address', 'match_ipv6_address', 'match_custom', - 'set_actions', 'description', 'tags', 'comments' + "routing_policy", + "index", + "action", + "continue_entry", + "match_community", + "match_community_list", + "match_ip_address", + "match_ipv6_address", + "match_custom", + "set_actions", + "description", + "tags", + "comments", ] class PrefixListFilterForm(NetBoxModelFilterSetForm): model = PrefixList - q = forms.CharField( - required=False, - label='Search' - ) + q = forms.CharField(required=False, label="Search") tag = TagFilterField(model) @@ -583,26 +573,26 @@ class PrefixListForm(NetBoxModelForm): class Meta: model = PrefixList - fields = ['name', 'description', 'family', 'tags', 'comments'] + fields = ["name", "description", "family", "tags", "comments"] class PrefixListRuleForm(NetBoxModelForm): prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), required=False, - help_text='NetBox Prefix Object', + help_text="NetBox Prefix Object", ) prefix_custom = IPNetworkFormField( required=False, - label='Prefix', - help_text='Just IP field for define special prefix like 0.0.0.0/0', + label="Prefix", + help_text="Just IP field for define special prefix like 0.0.0.0/0", ) ge = forms.IntegerField( - label='Greater than or equal to', + label="Greater than or equal to", required=False, ) le = forms.IntegerField( - label='Less than or equal to', + label="Less than or equal to", required=False, ) comments = CommentField() @@ -610,7 +600,13 @@ class PrefixListRuleForm(NetBoxModelForm): class Meta: model = PrefixListRule fields = [ - 'prefix_list', 'index', - 'action', 'prefix', 'prefix_custom', - 'ge', 'le', 'tags', 'comments' + "prefix_list", + "index", + "action", + "prefix", + "prefix_custom", + "ge", + "le", + "tags", + "comments", ] diff --git a/netbox_bgp/graphql/__init__.py b/netbox_bgp/graphql/__init__.py index 1b80483..40f45b7 100644 --- a/netbox_bgp/graphql/__init__.py +++ b/netbox_bgp/graphql/__init__.py @@ -1,3 +1,3 @@ -from .schema import (NetBoxBGPQuery) +from .schema import NetBoxBGPQuery -schema = [NetBoxBGPQuery] \ No newline at end of file +schema = [NetBoxBGPQuery] diff --git a/netbox_bgp/graphql/schema.py b/netbox_bgp/graphql/schema.py index d9439cc..1768637 100644 --- a/netbox_bgp/graphql/schema.py +++ b/netbox_bgp/graphql/schema.py @@ -14,7 +14,17 @@ CommunityList, CommunityListRule, ) -from .types import * +from .types import ( + CommunityType, + BGPSessionType, + BGPPeerGroupType, + RoutingPolicyType, + RoutingPolicyRuleType, + PrefixListType, + PrefixListRuleType, + CommunityListType, + CommunityListRuleType, +) @strawberry.type diff --git a/netbox_bgp/graphql/types.py b/netbox_bgp/graphql/types.py index 02230b1..716c01e 100644 --- a/netbox_bgp/graphql/types.py +++ b/netbox_bgp/graphql/types.py @@ -18,7 +18,17 @@ CommunityList, CommunityListRule, ) -from .filters import * +from .filters import ( + CommunityFilter, + BGPSessionFilter, + BGPPeerGroupFilter, + RoutingPolicyFilter, + RoutingPolicyRuleFilter, + PrefixListFilter, + PrefixListRuleFilter, + CommunityListFilter, + CommunityListRuleFilter, +) @strawberry_django.type(Community, fields="__all__", filters=CommunityFilter) @@ -26,7 +36,7 @@ class CommunityType(NetBoxObjectType): site: Annotated["SiteType", strawberry.lazy("dcim.graphql.types")] | None tenant: Annotated["TenantType", strawberry.lazy("tenancy.graphql.types")] | None status: str - # role = models.ForeignKey( + role: str description: str @@ -101,8 +111,6 @@ class RoutingPolicyRuleType(NetBoxObjectType): match_ipv6_address: List[ Annotated["PrefixType", strawberry.lazy("ipam.graphql.types")] ] - # match_custom = models.JSONField( - # set_actions = models.JSONField( @strawberry_django.type(PrefixList, fields="__all__", filters=PrefixListFilter) From c661a2cac13c566c24a8f03c06e21c5b7423cc67 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:58:59 +0000 Subject: [PATCH 18/44] fixed graphql schema with netbox_bgp as prefix name --- README.md | 1 + netbox_bgp/graphql/schema.py | 36 ++++++++++++++++++------------------ netbox_bgp/version.py | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 917a038..c4c58f3 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ This plugin provide following Models: | NetBox 3.5 | >= 0.10.0 | | NetBox 3.6 | >= 0.11.0 | | NetBox 3.7 | >= 0.12.0 | +| NetBox 4.0 | >= 0.13.0 | ## Installation diff --git a/netbox_bgp/graphql/schema.py b/netbox_bgp/graphql/schema.py index 1768637..9154578 100644 --- a/netbox_bgp/graphql/schema.py +++ b/netbox_bgp/graphql/schema.py @@ -30,55 +30,55 @@ @strawberry.type class NetBoxBGPQuery: @strawberry.field - def community(self, id: int) -> CommunityType: + def netbox_bgp_community(self, id: int) -> CommunityType: return Community.objects.get(pk=id) - community_list: List[CommunityType] = strawberry_django.field() + netbox_bgp_community_list: List[CommunityType] = strawberry_django.field() @strawberry.field - def bgp_session(self, id: int) -> BGPSessionType: + def netbox_bgp_session(self, id: int) -> BGPSessionType: return BGPSession.objects.get(pk=id) - bgp_session_list: List[BGPSessionType] = strawberry_django.field() + netbox_bgp_session_list: List[BGPSessionType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> BGPPeerGroupType: + def netbox_bgp_peer_group(self, id: int) -> BGPPeerGroupType: return BGPPeerGroup.objects.get(pk=id) - bgp_peer_group_list: List[BGPPeerGroupType] = strawberry_django.field() + netbox_bgp_peer_group_list: List[BGPPeerGroupType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> RoutingPolicyType: + def netbox_bgp_routing_policy(self, id: int) -> RoutingPolicyType: return RoutingPolicy.objects.get(pk=id) - routing_policies_list: List[RoutingPolicyType] = strawberry_django.field() + netbox_bgp_routing_policy_list: List[RoutingPolicyType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> RoutingPolicyRuleType: + def netbox_bgp_routing_policy_rule(self, id: int) -> RoutingPolicyRuleType: return RoutingPolicyRule.objects.get(pk=id) - routing_policies_rules_list: List[RoutingPolicyRuleType] = strawberry_django.field() + netbox_bgp_routing_policy_rule_list: List[RoutingPolicyRuleType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> PrefixListType: + def netbox_bgp_prefixlist(self, id: int) -> PrefixListType: return PrefixList.objects.get(pk=id) - prefixlist_list: List[PrefixListType] = strawberry_django.field() + netbox_bgp_prefixlist_list: List[PrefixListType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> PrefixListRuleType: + def netbox_bgp_prefixlist_rule(self, id: int) -> PrefixListRuleType: return PrefixListRule.objects.get(pk=id) - prefixlist_rules_list: List[PrefixListRuleType] = strawberry_django.field() + netbox_bgp_prefixlist_rule_list: List[PrefixListRuleType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> CommunityListType: + def netbox_bgp_communitylist(self, id: int) -> CommunityListType: return CommunityList.objects.get(pk=id) - communitylist_list: List[CommunityListType] = strawberry_django.field() + netbox_bgp_communitylist_list: List[CommunityListType] = strawberry_django.field() @strawberry.field - def bgp_peer_group(self, id: int) -> CommunityListRuleType: + def netbox_bgp_communitylist_rule(self, id: int) -> CommunityListRuleType: return CommunityListRule.objects.get(pk=id) - communitylist_rules_list: List[CommunityListRuleType] = strawberry_django.field() + netbox_bgp_communitylist_rule_list: List[CommunityListRuleType] = strawberry_django.field() diff --git a/netbox_bgp/version.py b/netbox_bgp/version.py index def467e..f23a6b3 100644 --- a/netbox_bgp/version.py +++ b/netbox_bgp/version.py @@ -1 +1 @@ -__version__ = "0.12.1" +__version__ = "0.13.0" From ab283befc5f73819b94278b304e34bd5a71c4a2f Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:07:54 +0000 Subject: [PATCH 19/44] fieldset for forms --- netbox_bgp/forms.py | 70 ++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index d8be76b..11ca9bf 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -1,5 +1,5 @@ from django import forms -from django.conf import settings +from utilities.forms.rendering import FieldSet from django.core.exceptions import ( MultipleObjectsReturned, ObjectDoesNotExist, @@ -188,6 +188,24 @@ class BGPSessionForm(NetBoxModelForm): ) comments = CommentField() + fieldsets = ( + FieldSet( + "name", + "description", + "site", + "device", + "status", + "peer_group", + "tenant", + "tags", + name="Session", + ), + FieldSet("remote_as", "remote_address", name="Remote"), + FieldSet("local_as", "local_address", name="Local"), + FieldSet("import_policies", "export_policies", name="Policies"), + FieldSet("prefix_list_in", "prefix_list_out", name="Prefixes"), + ) + class Meta: model = BGPSession fields = [ @@ -209,32 +227,7 @@ class Meta: "prefix_list_out", "comments", ] - fieldsets = ( - ( - "Session", - ( - "name", - "site", - "device", - "description", - "status", - "peer_group", - "tenant", - "tags", - ), - ), - ("Remote", ("remote_as", "remote_address")), - ("Local", ("local_as", "local_address")), - ( - "Policies", - ( - "import_policies", - "export_policies", - "prefix_list_in", - "prefix_list_out", - ), - ), - ) + widgets = { "status": forms.Select(), } @@ -431,20 +424,33 @@ class BGPSessionBulkEditForm(NetBoxModelBulkEditForm): ) model = BGPSession + fieldsets = ( - ( - ("Session"), - ("device", "site", "description", "status", "tenant", "peer_group"), + FieldSet( + "name", + "description", + "site", + "device", + "status", + "peer_group", + "tenant", + "tags", + name="Session", ), - (("AS"), ("local_as", "remote_as")), - (("Policies"), ("import_policies", "export_policies")), + FieldSet("remote_as", "remote_address", name="Remote"), + FieldSet("local_as", "local_address", name="Local"), + FieldSet("import_policies", "export_policies", name="Policies"), + FieldSet("prefix_list_in", "prefix_list_out", name="Prefixes"), ) + nullable_fields = [ "tenant", "description", "peer_group", "import_policies", "export_policies", + "prefix_list_in", + "prefix_list_out", ] From eeac1b3a48bfec9f32ef0ffe63b652378e740231 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:23:25 +0000 Subject: [PATCH 20/44] adding bulk import on menu item --- netbox_bgp/navigation.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index b001e03..c8f0915 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -11,10 +11,16 @@ buttons=( PluginMenuButton( link='plugins:netbox_bgp:community_add', - title='Communities', + title='Add', icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_community'], ), + PluginMenuButton( + link='plugins:netbox_bgp:community_import', + title='Import', + icon_class='mdi mdi-upload', + permissions=['netbox_bgp.add_community'], + ), ), ), PluginMenuItem( @@ -24,7 +30,7 @@ buttons=( PluginMenuButton( link='plugins:netbox_bgp:communitylist_add', - title='Community Lists', + title='Add', icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_communitylist'], ), @@ -37,10 +43,16 @@ buttons=( PluginMenuButton( link='plugins:netbox_bgp:bgpsession_add', - title='Sessions', + title='Add', icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_bgpsession'], ), + PluginMenuButton( + link='plugins:netbox_bgp:bgpsession_import', + title='Add', + icon_class='mdi mdi-upload', + permissions=['netbox_bgp.add_bgpsession'], + ) ), ), PluginMenuItem( @@ -50,7 +62,7 @@ buttons=( PluginMenuButton( link='plugins:netbox_bgp:routingpolicy_add', - title='Routing Policies', + title='Add', icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_routingpolicy'], ), @@ -63,7 +75,7 @@ buttons=( PluginMenuButton( link='plugins:netbox_bgp:prefixlist_add', - title='Prefix Lists', + title='Add', icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_prefixlist'], ), @@ -76,7 +88,7 @@ buttons=( PluginMenuButton( link='plugins:netbox_bgp:bgppeergroup_add', - title='Peer Groups', + title='Add', icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_bgppeergroup'], ), From 1b7f74e551caa0d61bc1a36c70c53de415023286 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:24:47 +0000 Subject: [PATCH 21/44] rename button import --- netbox_bgp/navigation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index c8f0915..fff2385 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -49,7 +49,7 @@ ), PluginMenuButton( link='plugins:netbox_bgp:bgpsession_import', - title='Add', + title='Import', icon_class='mdi mdi-upload', permissions=['netbox_bgp.add_bgpsession'], ) From 0ef74058734296e1bcc805e302ea4e1f94930c5b Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Thu, 18 Apr 2024 08:27:51 +0000 Subject: [PATCH 22/44] adding bulk buttons on session view --- netbox_bgp/tables.py | 2 +- netbox_bgp/views.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/netbox_bgp/tables.py b/netbox_bgp/tables.py index b455e48..10e1934 100644 --- a/netbox_bgp/tables.py +++ b/netbox_bgp/tables.py @@ -97,7 +97,7 @@ class Meta(NetBoxTable.Meta): fields = ( 'pk', 'name', 'device', 'local_address', 'local_as', 'remote_address', 'remote_as', 'description', 'peer_group', - 'site', 'status', 'tenant' + 'site', 'status', 'tenant', 'actions' ) default_columns = ( 'pk', 'name', 'device', 'local_address', 'local_as', diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index fcb03ec..7bd37b3 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -137,7 +137,6 @@ class BGPSessionListView(generic.ObjectListView): filterset = filtersets.BGPSessionFilterSet filterset_form = forms.BGPSessionFilterForm table = tables.BGPSessionTable - actions = {'add': {'add'}} class BGPSessionEditView(generic.ObjectEditView): From 9eab2e619042d4804b9fb8e79fd1d20f86b0452b Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Thu, 18 Apr 2024 08:37:58 +0000 Subject: [PATCH 23/44] removing overloaded buttons --- .../templates/netbox_bgp/bgpsession.html | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/netbox_bgp/templates/netbox_bgp/bgpsession.html b/netbox_bgp/templates/netbox_bgp/bgpsession.html index 23a6870..fa404ac 100644 --- a/netbox_bgp/templates/netbox_bgp/bgpsession.html +++ b/netbox_bgp/templates/netbox_bgp/bgpsession.html @@ -1,27 +1,11 @@ {% extends 'generic/object.html' %} -{% load buttons %} -{% load custom_links %} -{% load helpers %} {% load plugins %} {% load render_table from django_tables2 %} {% block breadcrumbs %}