From 5acdba4d54b5e8d4260a3f63455701f680795005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D0=B7=D0=B5=D1=84=D0=BE=D0=B2=D0=B8=D1=87=20=D0=9D?= =?UTF-8?q?=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B9=20=D0=98=D0=B3=D0=BE=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87?= Date: Thu, 28 Sep 2023 18:18:35 +0500 Subject: [PATCH 1/5] #154 fix --- netbox_bgp/api/serializers.py | 14 ++++-- netbox_bgp/tests/test_api.py | 82 ++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index f085dce..d0a0c61 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -8,6 +8,7 @@ from dcim.api.nested_serializers import NestedSiteSerializer, NestedDeviceSerializer from tenancy.api.nested_serializers import NestedTenantSerializer from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedASNSerializer, NestedPrefixSerializer +from ipam.api.field_serializers import IPNetworkField from netbox_bgp.models import ( @@ -31,7 +32,7 @@ def to_representation(self, value): class RoutingPolicySerializer(NetBoxModelSerializer): class Meta: model = RoutingPolicy - fields = '__all__' + fields = ['id', 'name', 'description'] class NestedRoutingPolicySerializer(WritableNestedSerializer): @@ -42,6 +43,7 @@ class Meta: fields = ['id', 'url', 'name', 'display', 'description'] validators = [] + class BGPPeerGroupSerializer(NetBoxModelSerializer): import_policies = SerializedPKRelatedField( queryset=RoutingPolicy.objects.all(), @@ -60,7 +62,7 @@ class BGPPeerGroupSerializer(NetBoxModelSerializer): class Meta: model = BGPPeerGroup - fields = '__all__' + fields = ['id', 'name', 'description', 'import_policies', 'export_policies'] class NestedBGPPeerGroupSerializer(WritableNestedSerializer): @@ -164,10 +166,12 @@ class Meta: model = PrefixList fields = ['id', 'url', 'display', 'name'] + class PrefixListSerializer(NetBoxModelSerializer): class Meta: model = PrefixList - fields = '__all__' + fields = ['id', 'name', 'description', 'family'] + class RoutingPolicyRuleSerializer(NetBoxModelSerializer): match_ip_address = SerializedPKRelatedField( @@ -189,11 +193,13 @@ class RoutingPolicyRuleSerializer(NetBoxModelSerializer): class Meta: model = RoutingPolicyRule - fields = '__all__' + fields = ['id', 'index', 'action', 'match_ip_address', 'routing_policy', 'match_community'] + class PrefixListRuleSerializer(NetBoxModelSerializer): prefix_list = NestedPrefixListSerializer() prefix = NestedPrefixSerializer(required=False, allow_null=True) + prefix_custom = IPNetworkField(required=False, allow_null=True) class Meta: model = PrefixListRule diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index 7dd998b..ee26ce4 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -14,7 +14,10 @@ from dcim.models import Site, DeviceRole, DeviceType, Manufacturer, Device, Interface from ipam.models import IPAddress, ASN, RIR -from netbox_bgp.models import Community, BGPPeerGroup, BGPSession +from netbox_bgp.models import ( + Community, BGPPeerGroup, BGPSession, + RoutingPolicy, RoutingPolicyRule, PrefixList, PrefixListRule +) class BaseTestCase(TestCase): @@ -278,4 +281,79 @@ def test_graphql_list(self): json.dumps({'query': query}), content_type='application/json' ) - self.assertEqual(response.status_code, status.HTTP_200_OK) \ No newline at end of file + self.assertEqual(response.status_code, status.HTTP_200_OK) + + +class RoutingPolicyTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self.base_url_lookup = 'plugins-api:netbox_bgp-api:routingpolicy' + self.rp = RoutingPolicy.objects.create(name='rp1', description='test_rp') + + def test_list_routing_policy(self): + url = reverse(f'{self.base_url_lookup}-list') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 1) + + def test_get_routing_policy(self): + url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.rp.pk}) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['name'], self.rp.name) + self.assertEqual(response.data['description'], self.rp.description) + + def test_create_routing_policy(self): + url = reverse(f'{self.base_url_lookup}-list') + data = {'name': 'testrp', 'description': 'test_rp1'} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(RoutingPolicy.objects.get(pk=response.data['id']).name, 'testrp') + self.assertEqual(RoutingPolicy.objects.get(pk=response.data['id']).description, 'test_rp1') + + +class PrefixListTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self.base_url_lookup = 'plugins-api:netbox_bgp-api:prefixlist' + self.obj = PrefixList.objects.create(name='pl1', description='test_pl', family='ipv4') + + def test_list_prefix_list(self): + url = reverse(f'{self.base_url_lookup}-list') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 1) + + def test_get_prefix_list(self): + url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.obj.pk}) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['name'], self.obj.name) + self.assertEqual(response.data['description'], self.obj.description) + + def test_create_prefix_list(self): + url = reverse(f'{self.base_url_lookup}-list') + data = {'name': 'testrp', 'description': 'test_rp1', 'family': 'ipv4'} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(PrefixList.objects.get(pk=response.data['id']).name, 'testrp') + self.assertEqual(PrefixList.objects.get(pk=response.data['id']).description, 'test_rp1') + + +class RoutingPolicyRuleTestCase(BaseTestCase): + pass + + +class PrefixListRuleTestCase(BaseTestCase): + pass + + +class TestAPISchema(BaseTestCase): + def setUp(self): + super().setUp() + self.base_url_lookup = 'schema' + + def test_api_schema(self): + url = reverse(f'{self.base_url_lookup}') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) From 7f979cbc2c1e3187148cfcdbaf226a9931f1994c Mon Sep 17 00:00:00 2001 From: Pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:00:11 +0200 Subject: [PATCH 2/5] updating use of tags from forms --- netbox_bgp/forms.py | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index 8f6be43..dd785e6 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -3,7 +3,6 @@ from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, ValidationError from django.utils.translation import gettext as _ -from extras.models import Tag from tenancy.models import Tenant from dcim.models import Device, Site from ipam.models import IPAddress, Prefix, ASN @@ -25,10 +24,6 @@ class CommunityForm(NetBoxModelForm): - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) status = forms.ChoiceField( required=False, choices=CommunityStatusChoices, @@ -114,10 +109,6 @@ class BGPSessionForm(NetBoxModelForm): max_length=64, required=True ) - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False @@ -280,10 +271,6 @@ class RoutingPolicyFilterForm(NetBoxModelFilterSetForm): class RoutingPolicyForm(NetBoxModelForm): - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) class Meta: model = RoutingPolicy @@ -315,10 +302,6 @@ class BGPPeerGroupForm(NetBoxModelForm): api_url='/api/plugins/bgp/routing-policy/' ) ) - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) class Meta: model = BGPPeerGroup @@ -361,7 +344,7 @@ class Meta: fields = [ 'routing_policy', 'index', 'action', 'continue_entry', 'match_community', 'match_ip_address', 'match_ipv6_address', 'match_custom', - 'set_actions', 'description', + 'set_actions', 'description', 'tags' ] @@ -376,10 +359,6 @@ class PrefixListFilterForm(NetBoxModelFilterSetForm): class PrefixListForm(NetBoxModelForm): - tags = DynamicModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False - ) class Meta: model = PrefixList @@ -411,5 +390,5 @@ class Meta: fields = [ 'prefix_list', 'index', 'action', 'prefix', 'prefix_custom', - 'ge', 'le' + 'ge', 'le', 'tags' ] From a0c76383a8490aef799c1ea9fb5a8065ad7c485f Mon Sep 17 00:00:00 2001 From: Pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:02:51 +0200 Subject: [PATCH 3/5] add site filter on bgp session + id on prefixlistrule --- netbox_bgp/filters.py | 19 ++++++++++++++++--- netbox_bgp/forms.py | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/netbox_bgp/filters.py b/netbox_bgp/filters.py index 6922fb5..6e768b5 100644 --- a/netbox_bgp/filters.py +++ b/netbox_bgp/filters.py @@ -7,7 +7,7 @@ from .models import Community, BGPSession, RoutingPolicy, RoutingPolicyRule, BGPPeerGroup, PrefixList, PrefixListRule from ipam.models import IPAddress, ASN -from dcim.models import Device +from dcim.models import Device, Site class CommunityFilterSet(NetBoxModelFilterSet): @@ -109,6 +109,18 @@ class BGPSessionFilterSet(NetBoxModelFilterSet): to_field_name='name', label='Device (name)', ) + site_id = django_filters.ModelMultipleChoiceFilter( + field_name='site__id', + queryset=Site.objects.all(), + to_field_name='id', + label='Site (ID)', + ) + site = django_filters.ModelMultipleChoiceFilter( + field_name='site__name', + queryset=Site.objects.all(), + to_field_name='name', + label='DSite (name)', + ) by_remote_address = django_filters.CharFilter( method='search_by_remote_ip', label='Remote Address', @@ -232,14 +244,15 @@ class PrefixListFilterSet(NetBoxModelFilterSet): class Meta: model = PrefixList - fields = ['name', 'description'] + fields = ['id', 'name', 'description'] def search(self, queryset, name, value): """Perform the filtered search.""" if not value.strip(): return queryset qs_filter = ( - Q(name__icontains=value) + Q(id__icontains=value) + | Q(name__icontains=value) | Q(description__icontains=value) ) return queryset.filter(qs_filter) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index dd785e6..063ab08 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -227,6 +227,11 @@ class BGPSessionFilterForm(NetBoxModelFilterSetForm): required=False, label=_('Device') ) + site_id = DynamicModelMultipleChoiceField( + queryset=Site.objects.all(), + required=False, + label=_('Site') + ) status = forms.MultipleChoiceField( choices=SessionStatusChoices, required=False, From 0293f872dbae7bcf2141c2e81dcfa5250e2e5f7a Mon Sep 17 00:00:00 2001 From: Pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:30:20 +0100 Subject: [PATCH 4/5] update use of filterset due to NetBoxModelFilterSet inheritance --- netbox_bgp/filters.py | 61 ++++++++----------------------------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/netbox_bgp/filters.py b/netbox_bgp/filters.py index 6e768b5..70dc396 100644 --- a/netbox_bgp/filters.py +++ b/netbox_bgp/filters.py @@ -11,34 +11,23 @@ class CommunityFilterSet(NetBoxModelFilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() class Meta: model = Community - fields = ['value', 'description', 'status', 'tenant'] + fields = ['id', 'value', 'description', 'status', 'tenant'] def search(self, queryset, name, value): """Perform the filtered search.""" if not value.strip(): return queryset qs_filter = ( - Q(id__icontains=value) - | Q(value__icontains=value) + Q(value__icontains=value) | Q(description__icontains=value) ) return queryset.filter(qs_filter) class BGPSessionFilterSet(NetBoxModelFilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() remote_as = django_filters.ModelMultipleChoiceFilter( field_name='remote_as__asn', @@ -132,7 +121,7 @@ class BGPSessionFilterSet(NetBoxModelFilterSet): class Meta: model = BGPSession - fields = ['name', 'description', 'status', 'tenant'] + fields = ['id', 'name', 'description', 'status', 'tenant'] def search(self, queryset, name, value): """Perform the filtered search.""" @@ -166,15 +155,10 @@ def search_by_local_ip(self, queryset, name, value): class RoutingPolicyFilterSet(NetBoxModelFilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() class Meta: model = RoutingPolicy - fields = ['name', 'description'] + fields = ['id', 'name', 'description'] def search(self, queryset, name, value): """Perform the filtered search.""" @@ -187,12 +171,7 @@ def search(self, queryset, name, value): return queryset.filter(qs_filter) -class RoutingPolicyRuleFilterSet(django_filters.FilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() +class RoutingPolicyRuleFilterSet(NetBoxModelFilterSet): class Meta: model = RoutingPolicyRule @@ -203,8 +182,7 @@ def search(self, queryset, name, value): if not value.strip(): return queryset qs_filter = ( - Q(id__icontains=value) - | Q(index__icontains=value) + Q(index__icontains=value) | Q(action__icontains=value) | Q(description__icontains=value) | Q(routing_policy_id__icontains=value) @@ -213,16 +191,11 @@ def search(self, queryset, name, value): return queryset.filter(qs_filter) -class BGPPeerGroupFilterSet(django_filters.FilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() +class BGPPeerGroupFilterSet(NetBoxModelFilterSet): class Meta: model = BGPPeerGroup - fields = ['name', 'description'] + fields = ['id', 'name', 'description'] def search(self, queryset, name, value): """Perform the filtered search.""" @@ -236,11 +209,6 @@ def search(self, queryset, name, value): class PrefixListFilterSet(NetBoxModelFilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() class Meta: model = PrefixList @@ -251,18 +219,12 @@ def search(self, queryset, name, value): if not value.strip(): return queryset qs_filter = ( - Q(id__icontains=value) - | Q(name__icontains=value) + Q(name__icontains=value) | Q(description__icontains=value) ) return queryset.filter(qs_filter) -class PrefixListRuleFilterSet(django_filters.FilterSet): - q = django_filters.CharFilter( - method='search', - label='Search', - ) - tag = TagFilter() +class PrefixListRuleFilterSet(NetBoxModelFilterSet): class Meta: model = PrefixListRule @@ -274,8 +236,7 @@ def search(self, queryset, name, value): if not value.strip(): return queryset qs_filter = ( - Q(id__icontains=value) - | Q(index__icontains=value) + Q(index__icontains=value) | Q(action__icontains=value) #| Q(prefix_custom__icontains=value) | Q(ge__icontains=value) From d655f6243165dd7d18304f00c28887c8d64af64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D0=B7=D0=B5=D1=84=D0=BE=D0=B2=D0=B8=D1=87=20=D0=9D?= =?UTF-8?q?=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B9=20=D0=98=D0=B3=D0=BE=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D0=B8=D1=87?= Date: Mon, 13 Nov 2023 11:48:28 +0500 Subject: [PATCH 5/5] ver bump --- netbox_bgp/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_bgp/version.py b/netbox_bgp/version.py index ae6db5f..fee46bd 100644 --- a/netbox_bgp/version.py +++ b/netbox_bgp/version.py @@ -1 +1 @@ -__version__ = "0.11.0" +__version__ = "0.11.1"