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 @@
- Prefix List + Community List
diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 9cf6798..9426528 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -104,7 +104,7 @@ class CommunityListDeleteView(generic.ObjectDeleteView): class CommunityListRuleListView(generic.ObjectListView): queryset = CommunityListRule.objects.all() - # filterset = RoutingPolicyRuleFilterSet + filterset = filtersets.CommunityListRuleFilterSet # filterset_form = RoutingPolicyRuleFilterForm table = tables.CommunityListRuleTable action_buttons = ('add',) @@ -278,7 +278,7 @@ def get_extra_context(self, request, instance): class RoutingPolicyRuleListView(generic.ObjectListView): queryset = RoutingPolicyRule.objects.all() - # filterset = RoutingPolicyRuleFilterSet + filterset = filtersets.RoutingPolicyRuleFilterSet # filterset_form = RoutingPolicyRuleFilterForm table = tables.RoutingPolicyRuleTable action_buttons = ('add',) @@ -384,7 +384,7 @@ class PrefixListDeleteView(generic.ObjectDeleteView): class PrefixListRuleListView(generic.ObjectListView): queryset = PrefixListRule.objects.all() - # filterset = RoutingPolicyRuleFilterSet + filterset = filtersets.PrefixListRuleFilterSet # filterset_form = RoutingPolicyRuleFilterForm table = tables.PrefixListRuleTable action_buttons = ('add',) From 89bc8b71cc116b46ba13714c767f9119039a73c9 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:39:53 +0100 Subject: [PATCH 02/44] standard test api on community list --- netbox_bgp/tests/test_api.py | 38 +++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index ff1029a..b31e979 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -16,7 +16,8 @@ from netbox_bgp.models import ( Community, BGPPeerGroup, BGPSession, - RoutingPolicy, RoutingPolicyRule, PrefixList, PrefixListRule + RoutingPolicy, RoutingPolicyRule, PrefixList, PrefixListRule, + CommunityList, CommunityListRule ) @@ -86,6 +87,41 @@ def test_graphql_list(self): self.assertEqual(response.status_code, status.HTTP_200_OK) +class CommunityListTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self.base_url_lookup = 'plugins-api:netbox_bgp-api:communitylist' + self.communitylist1 = CommunityList.objects.create(name='CL1', description='test_community_list1', comments='community_list_test') + + def test_list_community(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_community_list(self): + url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.communitylist1.pk}) + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['name'], self.communitylist1.name) + self.assertEqual(response.data['description'], self.communitylist1.description) + + def test_create_community(self): + url = reverse(f'{self.base_url_lookup}-list') + data = {'name': 'CL2', 'description': 'test_community_list2', 'comments': 'community_list_test2'} + response = self.client.post(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(CommunityList.objects.get(pk=response.data['id']).name, 'CL2') + self.assertEqual(CommunityList.objects.get(pk=response.data['id']).description, 'test_community_list2') + self.assertEqual(CommunityList.objects.get(pk=response.data['id']).comments, 'community_list_test2') + + def test_update_community_list(self): + pass + + def test_delete_community_list(self): + pass + + class PeerGroupTestCase(BaseTestCase): def setUp(self): super().setUp() From 18edd53b2bd6f7d125f62b990e69c12566d5f0ca Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:42:41 +0100 Subject: [PATCH 03/44] add test on model communitylist --- netbox_bgp/tests/test_models.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/netbox_bgp/tests/test_models.py b/netbox_bgp/tests/test_models.py index f7a0752..d48b169 100644 --- a/netbox_bgp/tests/test_models.py +++ b/netbox_bgp/tests/test_models.py @@ -6,7 +6,7 @@ from dcim.models import Site, Device, Manufacturer, DeviceRole, DeviceType from ipam.models import IPAddress, ASN, RIR -from netbox_bgp.models import BGPSession, Community, RoutingPolicy, BGPPeerGroup +from netbox_bgp.models import BGPSession, Community, CommunityList, RoutingPolicy, BGPPeerGroup class RoutingPolicyTestCase(TestCase): @@ -115,6 +115,26 @@ def test_invalid_community(self): self.assertRaises(ValidationError, community.full_clean) +class CommunityListTestCase(TestCase): + def setUp(self): + self.communitylist = CommunityList.objects.create( + name='community_list_1', + description='test_community_list', + comments='comment_cl1' + ) + + def test_create_community(self): + self.assertTrue(isinstance(self.communitylist, CommunityList)) + self.assertEqual(self.communitylist.__str__(), self.communitylist.name) + + def test_unique_together(self): + communitylist2 = CommunityList( + name='community_list_1', + description='test_community_list', + ) + with self.assertRaises(IntegrityError): + communitylist2.save() + class BGPSessionTestCase(TestCase): def setUp(self): manufacturer = Manufacturer.objects.create( From 50929952e8c9ca9b54f374dc24386fd83c2f647a Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 4 Mar 2024 08:54:19 +0100 Subject: [PATCH 04/44] add description on missing serializers --- netbox_bgp/api/serializers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index 2d03c8f..c939cf5 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -40,7 +40,7 @@ class NestedPrefixListSerializer(WritableNestedSerializer): class Meta: model = PrefixList - fields = ['id', 'url', 'display', 'name'] + fields = ['id', 'url', 'display', 'name', 'description'] class PrefixListSerializer(NetBoxModelSerializer): @@ -163,7 +163,7 @@ class NestedCommunitySerializer(WritableNestedSerializer): class Meta: model = Community fields = [ - 'id', 'url', 'display', 'value' + 'id', 'url', 'display', 'value', 'description' ] @@ -178,7 +178,7 @@ class NestedCommunityListSerializer(WritableNestedSerializer): class Meta: model = CommunityList - fields = ['id', 'url', 'display', 'name'] + fields = ['id', 'url', 'display', 'name', 'description'] class CommunityListRuleSerializer(NetBoxModelSerializer): @@ -190,7 +190,7 @@ class Meta: fields = [ 'id', 'tags', 'custom_fields', 'display', 'community_list', 'created', 'last_updated', - 'action', 'community', 'comments' + 'action', 'community', 'comments', 'description', ] @@ -231,6 +231,6 @@ class Meta: fields = [ 'id', 'tags', 'custom_fields', 'display', 'prefix_list', 'created', 'last_updated', - 'index', 'action', + 'index', 'action', 'description', 'prefix_custom', 'ge', 'le', 'prefix', 'comments' ] From 84485b7425282c5b6673ded664883264e390d45a Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 4 Mar 2024 09:48:02 +0100 Subject: [PATCH 05/44] add parameters on session api tests --- netbox_bgp/tests/test_api.py | 44 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index b31e979..624ef29 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -19,7 +19,7 @@ RoutingPolicy, RoutingPolicyRule, PrefixList, PrefixListRule, CommunityList, CommunityListRule ) - +from netbox_bgp.choices import IPAddressFamilyChoices, SessionStatusChoices class BaseTestCase(TestCase): def setUp(self): @@ -201,6 +201,12 @@ def setUp(self): self.remote_as = ASN.objects.create(asn=65001, rir=self.rir, description='remote_as') local_as = ASN.objects.create(asn=65002, rir=self.rir, description='local_as') remote_as = ASN.objects.create(asn=65003, rir=self.rir, description='remote_as') + self.import_pol = RoutingPolicy.objects.create(name="self_rm_in", description="self_desc_rm_in") + self.export_pol = RoutingPolicy.objects.create(name="self_rm_out", description="self_desc_rm_out") + import_pol = RoutingPolicy.objects.create(name="rm_in", description="desc_rm_in") + export_pol = RoutingPolicy.objects.create(name="rm_out", description="desc_rm_out") + self.prefix_in = PrefixList.objects.create(name="pl_in", description="desc_pl_in", family=IPAddressFamilyChoices.FAMILY_4) + self.prefix_out = PrefixList.objects.create(name="pl_out", description="desc_pl_out", family=IPAddressFamilyChoices.FAMILY_4) self.peer_group = BGPPeerGroup.objects.create(name='peer_group', description='peer_group_description') self.session = BGPSession.objects.create( name='session', @@ -209,10 +215,15 @@ def setUp(self): remote_as=remote_as, local_address=local_ip, remote_address=remote_ip, - status='active', + status=SessionStatusChoices.STATUS_ACTIVE, peer_group=self.peer_group, + prefix_list_in=self.prefix_in, + prefix_list_out=self.prefix_out, comments="comment_session_test" ) + self.session.save() + self.session.import_policies.add(import_pol) + self.session.export_policies.add(export_pol) def test_list_session(self): url = reverse(f'{self.base_url_lookup}-list') @@ -233,9 +244,16 @@ def test_get_session(self): self.assertEqual(response.data['status']['value'], self.session.status) self.assertEqual(response.data['peer_group']['name'], self.session.peer_group.name) self.assertEqual(response.data['peer_group']['description'], self.session.peer_group.description) + self.assertEqual(response.data['import_policies'][0]['name'], self.session.import_policies.all()[0].name) + self.assertEqual(response.data['import_policies'][0]['description'], self.session.import_policies.all()[0].description) + self.assertEqual(response.data['export_policies'][0]['name'], self.session.export_policies.all()[0].name) + self.assertEqual(response.data['export_policies'][0]['description'], self.session.export_policies.all()[0].description) + self.assertEqual(response.data['prefix_list_in']['name'], self.session.prefix_list_in.name) + self.assertEqual(response.data['prefix_list_in']['description'], self.session.prefix_list_in.description) + self.assertEqual(response.data['prefix_list_out']['name'], self.session.prefix_list_out.name) + self.assertEqual(response.data['prefix_list_out']['description'], self.session.prefix_list_out.description) self.assertEqual(response.data['comments'], self.session.comments) - def test_create_session(self): url = reverse(f'{self.base_url_lookup}-list') data = { @@ -248,14 +266,28 @@ def test_create_session(self): 'status': 'active', 'device': self.device.pk, 'peer_group': self.peer_group.pk, - 'comments': 'comment_session_test1' - + 'import_policies': [self.import_pol.pk], + 'export_policies': [self.export_pol.pk], + 'prefix_list_in': self.prefix_in.pk, + 'prefix_list_out': self.prefix_out.pk, + 'comments': 'comment_session_test1', } response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(BGPSession.objects.get(pk=response.data['id']).name, 'test_session') self.assertEqual(BGPSession.objects.get(pk=response.data['id']).description, 'session_descr') self.assertEqual(BGPSession.objects.get(pk=response.data['id']).comments, 'comment_session_test1') + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).local_as.id, self.local_as.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).remote_as.id, self.remote_as.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).device.id, self.device.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).status, self.session.status) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).local_address.id, self.local_ip.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).remote_address.id, self.remote_ip.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).import_policies.all()[0].id, self.import_pol.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).export_policies.all()[0].id, self.export_pol.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).prefix_list_in.id, self.prefix_in.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).prefix_list_out.id, self.prefix_out.pk) + self.assertEqual(BGPSession.objects.get(pk=response.data['id']).peer_group.id, self.peer_group.pk) def test_update_session(self): @@ -363,7 +395,7 @@ 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', comments='comments_pl') + self.obj = PrefixList.objects.create(name='pl1', description='test_pl', family=IPAddressFamilyChoices.FAMILY_4, comments='comments_pl') def test_list_prefix_list(self): url = reverse(f'{self.base_url_lookup}-list') From 9bd8f9a311ccd8549cc5981e5e3f7aaf091b74ed Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sun, 17 Mar 2024 16:25:23 +0100 Subject: [PATCH 06/44] add device on session test api --- netbox_bgp/tests/test_api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index 624ef29..e024e63 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -211,6 +211,7 @@ def setUp(self): self.session = BGPSession.objects.create( name='session', description='session_descr', + device=self.device, local_as=local_as, remote_as=remote_as, local_address=local_ip, @@ -237,6 +238,7 @@ def test_get_session(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['name'], self.session.name) self.assertEqual(response.data['description'], self.session.description) + self.assertEqual(response.data['device']['name'], self.device.name) self.assertEqual(response.data['local_as']['asn'], self.session.local_as.asn) self.assertEqual(response.data['remote_as']['asn'], self.session.remote_as.asn) self.assertEqual(response.data['local_address']['address'], self.session.local_address.address) From e21843dc3cc67b2a2fe59ef2c5407191893d0d84 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:10:52 +0000 Subject: [PATCH 07/44] moving extras.plugins to netbox.plugins --- netbox_bgp/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netbox_bgp/__init__.py b/netbox_bgp/__init__.py index e30191d..a813e84 100644 --- a/netbox_bgp/__init__.py +++ b/netbox_bgp/__init__.py @@ -1,4 +1,4 @@ -from extras.plugins import PluginConfig +from netbox.plugins import PluginConfig from .version import __version__ @@ -11,8 +11,8 @@ class BGPConfig(PluginConfig): author_email = 'mgk.kolek@gmail.com' base_url = 'bgp' required_settings = [] - min_version = '3.5.0' - max_version = '3.7.99' + min_version = '3.6.0' + max_version = '4.0.99' default_settings = { 'device_ext_page': 'right', 'top_level_menu' : False, From e0bb0e24cd21b2bd41fca5fa1d39b6ec801cf2d1 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:11:22 +0000 Subject: [PATCH 08/44] removing color from navigation --- netbox_bgp/navigation.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index 430f38d..8d36a83 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -1,7 +1,6 @@ from django.conf import settings from extras.plugins import PluginMenuButton, PluginMenuItem, PluginMenu -from utilities.choices import ButtonColorChoices _menu_items = ( @@ -14,7 +13,6 @@ link='plugins:netbox_bgp:community_add', title='Communities', icon_class='mdi mdi-plus-thick', - color=ButtonColorChoices.GREEN, permissions=['netbox_bgp.add_community'], ), ), @@ -28,7 +26,6 @@ link='plugins:netbox_bgp:communitylist_add', title='Community Lists', icon_class='mdi mdi-plus-thick', - color=ButtonColorChoices.GREEN, permissions=['netbox_bgp.add_communitylist'], ), ), @@ -42,7 +39,6 @@ link='plugins:netbox_bgp:bgpsession_add', title='Sessions', icon_class='mdi mdi-plus-thick', - color=ButtonColorChoices.GREEN, permissions=['netbox_bgp.add_bgpsession'], ), ), @@ -56,7 +52,6 @@ link='plugins:netbox_bgp:routingpolicy_add', title='Routing Policies', icon_class='mdi mdi-plus-thick', - color=ButtonColorChoices.GREEN, permissions=['netbox_bgp.add_routingpolicy'], ), ), @@ -70,7 +65,6 @@ link='plugins:netbox_bgp:prefixlist_add', title='Prefix Lists', icon_class='mdi mdi-plus-thick', - color=ButtonColorChoices.GREEN, permissions=['netbox_bgp.add_prefixlist'], ), ), @@ -84,7 +78,6 @@ link='plugins:netbox_bgp:bgppeergroup_add', title='Peer Groups', icon_class='mdi mdi-plus-thick', - color=ButtonColorChoices.GREEN, permissions=['netbox_bgp.add_bgppeergroup'], ), ), From 906f2f8bae347aaceaa8b44bbb8c83536c60fa7d Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:19:03 +0000 Subject: [PATCH 09/44] plugin won t be compatible with 3.x version --- netbox_bgp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_bgp/__init__.py b/netbox_bgp/__init__.py index a813e84..d1c7bdf 100644 --- a/netbox_bgp/__init__.py +++ b/netbox_bgp/__init__.py @@ -11,7 +11,7 @@ class BGPConfig(PluginConfig): author_email = 'mgk.kolek@gmail.com' base_url = 'bgp' required_settings = [] - min_version = '3.6.0' + min_version = '4.0.0' max_version = '4.0.99' default_settings = { 'device_ext_page': 'right', From e2ccf6dd41322210d43d121ccc5a2ff6fcd72bd6 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 30 Mar 2024 19:31:23 +0000 Subject: [PATCH 10/44] remove extras package dependencies --- netbox_bgp/__init__.py | 2 +- netbox_bgp/filtersets.py | 1 - netbox_bgp/navigation.py | 2 +- netbox_bgp/template_content.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/netbox_bgp/__init__.py b/netbox_bgp/__init__.py index d1c7bdf..f3645dc 100644 --- a/netbox_bgp/__init__.py +++ b/netbox_bgp/__init__.py @@ -11,7 +11,7 @@ class BGPConfig(PluginConfig): author_email = 'mgk.kolek@gmail.com' base_url = 'bgp' required_settings = [] - min_version = '4.0.0' + min_version = '4.0.0-dev' max_version = '4.0.99' default_settings = { 'device_ext_page': 'right', diff --git a/netbox_bgp/filtersets.py b/netbox_bgp/filtersets.py index 4f9f455..55a7651 100644 --- a/netbox_bgp/filtersets.py +++ b/netbox_bgp/filtersets.py @@ -2,7 +2,6 @@ import netaddr from django.db.models import Q from netaddr.core import AddrFormatError -from extras.filters import TagFilter from netbox.filtersets import NetBoxModelFilterSet from .models import ( diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index 8d36a83..b001e03 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -1,6 +1,6 @@ from django.conf import settings -from extras.plugins import PluginMenuButton, PluginMenuItem, PluginMenu +from netbox.plugins import PluginMenuButton, PluginMenuItem, PluginMenu _menu_items = ( diff --git a/netbox_bgp/template_content.py b/netbox_bgp/template_content.py index 57198a9..ead17ff 100644 --- a/netbox_bgp/template_content.py +++ b/netbox_bgp/template_content.py @@ -1,4 +1,4 @@ -from extras.plugins import PluginTemplateExtension +from netbox.plugins import PluginTemplateExtension from .models import BGPSession from .tables import BGPSessionTable From 90814f5cf214bc134b4fe2d69e68d7685a71b2a4 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 30 Mar 2024 20:00:38 +0000 Subject: [PATCH 11/44] update action on views --- netbox_bgp/views.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 9cf6798..fcb03ec 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -23,8 +23,7 @@ class CommunityListView(generic.ObjectListView): filterset = filtersets.CommunityFilterSet filterset_form = forms.CommunityFilterForm table = tables.CommunityTable - action_buttons = ('add',) - + actions = {'add': {'add'}} class CommunityView(generic.ObjectView): queryset = Community.objects.all() @@ -66,7 +65,7 @@ class CommunityListListView(generic.ObjectListView): filterset = filtersets.CommunityListFilterSet filterset_form = forms.CommunityListFilterForm table = tables.CommunityListTable - action_buttons = ('add',) + actions = {'add': {'add'}} class CommunityListEditView(generic.ObjectEditView): @@ -107,7 +106,7 @@ class CommunityListRuleListView(generic.ObjectListView): # filterset = RoutingPolicyRuleFilterSet # filterset_form = RoutingPolicyRuleFilterForm table = tables.CommunityListRuleTable - action_buttons = ('add',) + actions = {'add': {'add'}} class CommunityListRuleEditView(generic.ObjectEditView): @@ -138,7 +137,7 @@ class BGPSessionListView(generic.ObjectListView): filterset = filtersets.BGPSessionFilterSet filterset_form = forms.BGPSessionFilterForm table = tables.BGPSessionTable - action_buttons = ('add',) + actions = {'add': {'add'}} class BGPSessionEditView(generic.ObjectEditView): @@ -205,7 +204,7 @@ class RoutingPolicyListView(generic.ObjectListView): filterset = filtersets.RoutingPolicyFilterSet filterset_form = forms.RoutingPolicyFilterForm table = tables.RoutingPolicyTable - action_buttons = ('add',) + actions = {'add': {'add'}} class RoutingPolicyEditView(generic.ObjectEditView): @@ -281,7 +280,7 @@ class RoutingPolicyRuleListView(generic.ObjectListView): # filterset = RoutingPolicyRuleFilterSet # filterset_form = RoutingPolicyRuleFilterForm table = tables.RoutingPolicyRuleTable - action_buttons = ('add',) + actions = {'add': {'add'}} # Peer Group @@ -292,7 +291,7 @@ class BGPPeerGroupListView(generic.ObjectListView): filterset = filtersets.BGPPeerGroupFilterSet filterset_form = forms.BGPPeerGroupFilterForm table = tables.BGPPeerGroupTable - action_buttons = ('add',) + actions = {'add': {'add'}} class BGPPeerGroupEditView(generic.ObjectEditView): @@ -342,7 +341,7 @@ class PrefixListListView(generic.ObjectListView): filterset = filtersets.PrefixListFilterSet filterset_form = forms.PrefixListFilterForm table = tables.PrefixListTable - action_buttons = ('add',) + actions = {'add': {'add'}} class PrefixListEditView(generic.ObjectEditView): @@ -387,7 +386,7 @@ class PrefixListRuleListView(generic.ObjectListView): # filterset = RoutingPolicyRuleFilterSet # filterset_form = RoutingPolicyRuleFilterForm table = tables.PrefixListRuleTable - action_buttons = ('add',) + actions = {'add': {'add'}} class PrefixListRuleEditView(generic.ObjectEditView): From fa42362486d97b47f9c0a1b7a92206c9ad86a683 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Thu, 4 Apr 2024 18:24:41 +0000 Subject: [PATCH 12/44] update nested serializer + black format --- netbox_bgp/api/serializers.py | 325 ++++++++++++++++++++-------------- 1 file changed, 195 insertions(+), 130 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index cc846bb..8b46b2d 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -1,246 +1,311 @@ 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 -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 ipam.api.serializers import IPAddressSerializer +from tenancy.api.serializers import TenantSerializer +from dcim.api.serializers import SiteSerializer, DeviceSerializer +from ipam.api.field_serializers import IPNetworkField from netbox_bgp.models import ( - BGPSession, RoutingPolicy, BGPPeerGroup, - Community, RoutingPolicyRule, PrefixList, - PrefixListRule, CommunityList, CommunityListRule + BGPSession, + RoutingPolicy, + BGPPeerGroup, + Community, + RoutingPolicyRule, + PrefixList, + PrefixListRule, + CommunityList, + CommunityListRule, ) 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'] - - -class NestedRoutingPolicySerializer(WritableNestedSerializer): - url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:routingpolicy') + url = HyperlinkedIdentityField(view_name="plugins:netbox_bgp:routingpolicy") class Meta: model = RoutingPolicy - fields = ['id', 'url', 'name', 'display', 'description'] - validators = [] - - -class NestedPrefixListSerializer(WritableNestedSerializer): - url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:prefixlist') - - class Meta: - model = PrefixList - fields = ['id', 'url', 'display', 'name'] + fields = [ + "id", + "url", + "name", + "description", + "tags", + "custom_fields", + "comments", + ] + brief_fields = ("id", "url", "display", "name", "description") class PrefixListSerializer(NetBoxModelSerializer): + url = HyperlinkedIdentityField(view_name="plugins:netbox_bgp:prefixlist") + class Meta: model = PrefixList - fields = ['id', 'name', 'display','description', 'family', 'tags', 'custom_fields', 'comments'] + fields = [ + "id", + "url", + "name", + "display", + "description", + "family", + "tags", + "custom_fields", + "comments", + ] + brief_fields = ("id", "url", "display", "name", "description") class BGPPeerGroupSerializer(NetBoxModelSerializer): + url = HyperlinkedIdentityField(view_name="plugins:netbox_bgp:bgppeergroup") + import_policies = SerializedPKRelatedField( queryset=RoutingPolicy.objects.all(), - serializer=NestedRoutingPolicySerializer, + serializer=RoutingPolicySerializer, + nested=True, required=False, allow_null=True, - many=True + many=True, ) export_policies = SerializedPKRelatedField( queryset=RoutingPolicy.objects.all(), - serializer=NestedRoutingPolicySerializer, + serializer=RoutingPolicySerializer, + nested=True, required=False, allow_null=True, - many=True + many=True, ) class Meta: model = BGPPeerGroup - fields = ['id', 'display', 'name', 'description', 'import_policies', 'export_policies', 'comments'] - - -class NestedBGPPeerGroupSerializer(WritableNestedSerializer): - url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:bgppeergroup') - - class Meta: - model = BGPPeerGroup - fields = ['id', 'display', 'url', 'name', 'description'] - validators = [] + fields = [ + "id", + "url", + "display", + "name", + "description", + "import_policies", + "export_policies", + "comments", + ] + brief_fields = ("id", "url", "display", "name", "description") class BGPSessionSerializer(NetBoxModelSerializer): + url = HyperlinkedIdentityField(view_name="plugins:netbox_bgp:bgpsession") status = ChoiceField(choices=SessionStatusChoices, required=False) - site = NestedSiteSerializer(required=False, allow_null=True) - tenant = NestedTenantSerializer(required=False, allow_null=True) - device = NestedDeviceSerializer(required=False, allow_null=True) - local_address = NestedIPAddressSerializer(required=True, allow_null=False) - remote_address = NestedIPAddressSerializer(required=True, allow_null=False) - local_as = NestedASNSerializer(required=True, allow_null=False) - remote_as = NestedASNSerializer(required=True, allow_null=False) - peer_group = NestedBGPPeerGroupSerializer(required=False, allow_null=True) - prefix_list_in = NestedPrefixListSerializer(required=False, allow_null=True) - prefix_list_out = NestedPrefixListSerializer(required=False, allow_null=True) + site = SiteSerializer(nested=True, required=False, allow_null=True) + tenant = TenantSerializer(nested=True, required=False, allow_null=True) + device = DeviceSerializer(nested=True, required=False, allow_null=True) + local_address = IPAddressSerializer(nested=True, required=True, allow_null=False) + remote_address = IPAddressSerializer(nested=True, required=True, allow_null=False) + local_as = ASNSerializer(nested=True, required=True, allow_null=False) + remote_as = ASNSerializer(nested=True, required=True, allow_null=False) + peer_group = BGPPeerGroupSerializer(nested=True, required=False, allow_null=True) + prefix_list_in = PrefixListSerializer(nested=True, required=False, allow_null=True) + prefix_list_out = PrefixListSerializer(nested=True, required=False, allow_null=True) import_policies = SerializedPKRelatedField( queryset=RoutingPolicy.objects.all(), - serializer=NestedRoutingPolicySerializer, + serializer=RoutingPolicySerializer, + nested=True, required=False, allow_null=True, - many=True + many=True, ) export_policies = SerializedPKRelatedField( queryset=RoutingPolicy.objects.all(), - serializer=NestedRoutingPolicySerializer, + serializer=RoutingPolicySerializer, + nested=True, required=False, allow_null=True, - many=True + many=True, ) class Meta: model = BGPSession fields = [ - 'id', 'tags', 'custom_fields', - 'display', 'status', 'site', 'tenant', - 'device', 'local_address', 'remote_address', - 'local_as', 'remote_as', 'peer_group', 'import_policies', - 'export_policies', 'prefix_list_in','prefix_list_out', - 'created', 'last_updated', - 'name', 'description', 'comments' - ] - + "id", + "url", + "tags", + "custom_fields", + "display", + "status", + "site", + "tenant", + "device", + "local_address", + "remote_address", + "local_as", + "remote_as", + "peer_group", + "import_policies", + "export_policies", + "prefix_list_in", + "prefix_list_out", + "created", + "last_updated", + "name", + "description", + "comments", + ] def to_representation(self, instance): ret = super().to_representation(instance) if instance is not None: if instance.peer_group: - for pol in instance.peer_group.import_policies.difference(instance.import_policies.all()): - ret['import_policies'].append( - NestedRoutingPolicySerializer(pol, context={'request': self.context['request']}).data + for pol in instance.peer_group.import_policies.difference( + instance.import_policies.all() + ): + ret["import_policies"].append( + RoutingPolicySerializer( + pol, + context={"request": self.context["request"]}, + nested=True, + ).data ) - for pol in instance.peer_group.export_policies.difference(instance.export_policies.all()): - ret['export_policies'].append( - NestedRoutingPolicySerializer(pol, context={'request': self.context['request']}).data + for pol in instance.peer_group.export_policies.difference( + instance.export_policies.all() + ): + ret["export_policies"].append( + RoutingPolicySerializer( + pol, + context={"request": self.context["request"]}, + nested=True, + ).data ) return ret -class NestedBGPSessionSerializer(WritableNestedSerializer): - url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:bgpsession') - - class Meta: - model = BGPSession - fields = ['id', 'url', 'name', 'display','description'] - validators = [] - - class CommunitySerializer(NetBoxModelSerializer): status = ChoiceField(choices=CommunityStatusChoices, required=False) - tenant = NestedTenantSerializer(required=False, allow_null=True) - - class Meta: - model = Community - fields = [ - 'id', 'tags', 'custom_fields', 'display', - 'status', 'tenant', 'created', 'last_updated', - 'description', - 'value', 'site', 'role', 'comments' - ] - -class NestedCommunitySerializer(WritableNestedSerializer): - url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:community') + tenant = TenantSerializer(nested=True, required=False, allow_null=True) + url = HyperlinkedIdentityField(view_name="plugins:netbox_bgp:community") class Meta: model = Community fields = [ - 'id', 'url', 'display', 'value' + "id", + "url", + "tags", + "custom_fields", + "display", + "status", + "tenant", + "created", + "last_updated", + "description", + "value", + "site", + "role", + "comments", ] + brief_fields = ("id", "url", "display", "value", "description") class CommunityListSerializer(NetBoxModelSerializer): - class Meta: - model = CommunityList - fields = ['id', 'name', 'display','description', 'tags', 'custom_fields', 'comments'] - - -class NestedCommunityListSerializer(WritableNestedSerializer): - url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:communitylist') + url = HyperlinkedIdentityField(view_name="plugins:netbox_bgp:communitylist") class Meta: model = CommunityList - fields = ['id', 'url', 'display', 'name'] + fields = [ + "id", + "url", + "name", + "display", + "description", + "tags", + "custom_fields", + "comments", + ] + brief_fields = ("id", "url", "display", "name", "description") class CommunityListRuleSerializer(NetBoxModelSerializer): - community_list = NestedCommunityListSerializer() - community = NestedCommunitySerializer(required=False, allow_null=True) + community_list = CommunityListSerializer(nested=True) + community = CommunitySerializer(nested=True, required=False, allow_null=True) class Meta: model = CommunityListRule fields = [ - 'id', 'tags', 'custom_fields', 'display', - 'community_list', 'created', 'last_updated', - 'action', 'community', 'comments' + "id", + "tags", + "custom_fields", + "display", + "community_list", + "created", + "last_updated", + "action", + "community", + "comments", ] class RoutingPolicyRuleSerializer(NetBoxModelSerializer): match_ip_address = SerializedPKRelatedField( queryset=PrefixList.objects.all(), - serializer=NestedPrefixListSerializer, + serializer=PrefixListSerializer, + nested=True, required=False, allow_null=True, - many=True + many=True, ) - routing_policy = NestedRoutingPolicySerializer() + routing_policy = RoutingPolicySerializer(nested=True) match_community = SerializedPKRelatedField( queryset=Community.objects.all(), - serializer=NestedCommunitySerializer, + serializer=CommunitySerializer, + nested=True, required=False, allow_null=True, - many=True + many=True, ) - class Meta: model = RoutingPolicyRule fields = [ - 'id', 'index', 'display' ,'action', 'match_ip_address', - 'routing_policy', 'match_community', 'match_custom', 'set_actions', - 'match_ipv6_address', 'description', 'tags', 'custom_fields', 'comments' + "id", + "index", + "display", + "action", + "match_ip_address", + "routing_policy", + "match_community", + "match_custom", + "set_actions", + "match_ipv6_address", + "description", + "tags", + "custom_fields", + "comments", ] class PrefixListRuleSerializer(NetBoxModelSerializer): - prefix_list = NestedPrefixListSerializer() - prefix = NestedPrefixSerializer(required=False, allow_null=True) + prefix_list = PrefixListSerializer(nested=True) + prefix = PrefixSerializer(nested=True, required=False, allow_null=True) prefix_custom = IPNetworkField(required=False, allow_null=True) class Meta: model = PrefixListRule fields = [ - 'id', 'tags', 'custom_fields', 'display', - 'prefix_list', 'created', 'last_updated', - 'index', 'action', - 'prefix_custom', 'ge', 'le', 'prefix', 'comments' + "id", + "tags", + "custom_fields", + "display", + "prefix_list", + "created", + "last_updated", + "index", + "action", + "prefix_custom", + "ge", + "le", + "prefix", + "comments", ] From cf6de8f655044ef38aa4502406b2b96a6c71beb5 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:15:30 +0000 Subject: [PATCH 13/44] adding serializer from ipam.api.serializers --- netbox_bgp/api/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index 8b46b2d..1875d10 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -2,10 +2,9 @@ from rest_framework.relations import PrimaryKeyRelatedField from netbox.api.fields import ChoiceField, SerializedPKRelatedField -from netbox.api.serializers.nested import WritableNestedSerializer from netbox.api.serializers import NetBoxModelSerializer -from ipam.api.serializers import IPAddressSerializer +from ipam.api.serializers import IPAddressSerializer, ASNSerializer, PrefixSerializer from tenancy.api.serializers import TenantSerializer from dcim.api.serializers import SiteSerializer, DeviceSerializer From 2ff0cccfa2d0ad211566832672c33d7ca0d7a253 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 8 Apr 2024 07:08:08 +0000 Subject: [PATCH 14/44] redrafting tests api --- netbox_bgp/api/serializers.py | 14 + netbox_bgp/filtersets.py | 2 +- netbox_bgp/tests/test_api.py | 938 +++++++++++++++++++++------------- 3 files changed, 602 insertions(+), 352 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index 1875d10..e3886af 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -33,6 +33,7 @@ class Meta: fields = [ "id", "url", + "display", "name", "description", "tags", @@ -153,6 +154,7 @@ class Meta: "description", "comments", ] + brief_fields = ("id", "url", "display", "name", "description") def to_representation(self, instance): ret = super().to_representation(instance) @@ -264,6 +266,14 @@ class RoutingPolicyRuleSerializer(NetBoxModelSerializer): allow_null=True, many=True, ) + match_community_list = SerializedPKRelatedField( + queryset=CommunityList.objects.all(), + serializer=CommunityListSerializer, + nested=True, + required=False, + allow_null=True, + many=True, + ) class Meta: model = RoutingPolicyRule @@ -275,6 +285,7 @@ class Meta: "match_ip_address", "routing_policy", "match_community", + "match_community_list", "match_custom", "set_actions", "match_ipv6_address", @@ -283,6 +294,7 @@ class Meta: "custom_fields", "comments", ] + brief_fields = ("id", "display", "description") class PrefixListRuleSerializer(NetBoxModelSerializer): @@ -294,6 +306,7 @@ class Meta: model = PrefixListRule fields = [ "id", + "description", "tags", "custom_fields", "display", @@ -308,3 +321,4 @@ class Meta: "prefix", "comments", ] + brief_fields = ("id", "display", "description") diff --git a/netbox_bgp/filtersets.py b/netbox_bgp/filtersets.py index 55a7651..b12b6ff 100644 --- a/netbox_bgp/filtersets.py +++ b/netbox_bgp/filtersets.py @@ -250,7 +250,7 @@ class PrefixListFilterSet(NetBoxModelFilterSet): class Meta: model = PrefixList - fields = ['id', 'name', 'description'] + fields = ['id', 'name', 'description', 'family'] def search(self, queryset, name, value): """Perform the filtered search.""" diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index ff1029a..59afbe3 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -1,372 +1,608 @@ import json -from django.contrib.auth.models import User -from django.test import TestCase -from django.test.client import Client from django.urls import reverse -from rest_framework import status -from rest_framework.test import APIClient, APITestCase - - -from users.models import Token +from utilities.testing import APITestCase, APIViewTestCases from tenancy.models import Tenant from dcim.models import Site, DeviceRole, DeviceType, Manufacturer, Device, Interface -from ipam.models import IPAddress, ASN, RIR +from ipam.models import IPAddress, ASN, RIR, Prefix from netbox_bgp.models import ( - Community, BGPPeerGroup, BGPSession, - RoutingPolicy, RoutingPolicyRule, PrefixList, PrefixListRule + Community, + CommunityList, + BGPPeerGroup, + BGPSession, + RoutingPolicy, + RoutingPolicyRule, + PrefixList, + PrefixListRule, ) +from netbox_bgp.choices import ( + SessionStatusChoices, + IPAddressFamilyChoices, + ActionChoices, +) -class BaseTestCase(TestCase): - def setUp(self): - self.user = User.objects.create(username='testuser', is_superuser=True) - self.token = Token.objects.create(user=self.user) - # todo change to Client - self.client = APIClient() - self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}') - self.gql_client = Client(HTTP_AUTHORIZATION=f'Token {self.token.key}') - - -class CommunityTestCase(BaseTestCase): - def setUp(self): - super().setUp() - self.base_url_lookup = 'plugins-api:netbox_bgp-api:community' - self.community1 = Community.objects.create(value='65000:65000', description='test_community', comments='community_test') - - def test_list_community(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_community(self): - url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.community1.pk}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['value'], self.community1.value) - self.assertEqual(response.data['description'], self.community1.description) - - def test_create_community(self): - url = reverse(f'{self.base_url_lookup}-list') - data = {'value': '65001:65001', 'description': 'test_community1', 'comments': 'community_test1'} - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(Community.objects.get(pk=response.data['id']).value, '65001:65001') - self.assertEqual(Community.objects.get(pk=response.data['id']).description, 'test_community1') - self.assertEqual(Community.objects.get(pk=response.data['id']).comments, 'community_test1') - - def test_update_community(self): - pass - - def test_delete_community(self): - pass - - def test_graphql(self): - url = reverse('graphql') - query = 'query community($id: Int!){community(id: $id){value}}' - response = self.gql_client.post( - url, - json.dumps({'query': query, 'variables': {'id': self.community1.pk}}), - content_type='application/json' + +class CommunityAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = Community + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id", "url", "value"] + + create_data = [ + {"value": "65001:65000"}, + {"value": "65002:65001"}, + {"value": "65003:65002"}, + ] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + communities = ( + Community(value="65000:65000"), + Community(value="65000:65001"), + Community(value="65000:65002"), ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(json.loads(response.content)['data']['community']['value'], self.community1.value) - - def test_graphql_list(self): - url = reverse('graphql') - query = '{community_list{value}}' - response = self.gql_client.post( - url, - json.dumps({'query': query}), - content_type='application/json' + Community.objects.bulk_create(communities) + + +# # def test_graphql(self): +# # url = reverse('graphql') +# # query = 'query community($id: Int!){community(id: $id){value}}' +# # response = self.gql_client.post( +# # url, +# # json.dumps({'query': query, 'variables': {'id': self.community1.pk}}), +# # content_type='application/json' +# # ) +# # self.assertEqual(response.status_code, 200) +# # self.assertEqual(json.loads(response.content)['data']['community']['value'], self.community1.value) + +# # def test_graphql_list(self): +# # url = reverse('graphql') +# # query = '{community_list{value}}' +# # response = self.gql_client.post( +# # url, +# # json.dumps({'query': query}), +# # content_type='application/json' +# # ) +# # self.assertEqual(response.status_code, 200) + + +class BGPPeerGroupAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = BGPPeerGroup + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id", "name", "url"] + + create_data = [ + { + "name": "test_peer_group", + "description": "peer_group_desc", + "comments": "peer_group_comment1", + }, + { + "name": "test_peer_group2", + "description": "peer_group_desc2", + "comments": "peer_group_comment2", + }, + { + "name": "test_peer_group3", + "description": "peer_group_desc3", + "comments": "peer_group_comment3", + }, + ] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + peer_groups = ( + BGPPeerGroup( + name="peer group 1", description="peer group 1", comments="peer group 1" + ), + BGPPeerGroup( + name="peer group 2", description="peer group 2", comments="peer group 2" + ), + BGPPeerGroup( + name="peer group 3", description="peer group 3", comments="peer group 3" + ), ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - - -class PeerGroupTestCase(BaseTestCase): - def setUp(self): - super().setUp() - self.base_url_lookup = 'plugins-api:netbox_bgp-api:peergroup' - self.peer_group = BGPPeerGroup.objects.create(name='peer_group', description='peer_group_description', comments='peer_group_comment') - - def test_list_peer_group(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_peer_group(self): - url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.peer_group.pk}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['name'], self.peer_group.name) - self.assertEqual(response.data['description'], self.peer_group.description) - - def test_create_peer_group(self): - url = reverse(f'{self.base_url_lookup}-list') - data = {'name': 'test_peer_group', 'description': 'peer_group_desc', 'comments': 'peer_group_comment1'} - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(BGPPeerGroup.objects.get(pk=response.data['id']).name, 'test_peer_group') - self.assertEqual(BGPPeerGroup.objects.get(pk=response.data['id']).description, 'peer_group_desc') - self.assertEqual(BGPPeerGroup.objects.get(pk=response.data['id']).comments, 'peer_group_comment1') - - def test_update_peer_group(self): - pass - - def test_delete_peer_group(self): - pass - - def test_graphql(self): - url = reverse('graphql') - query = 'query peer_group($id: Int!){peer_group(id: $id){name}}' - response = self.gql_client.post( - url, - json.dumps({'query': query, 'variables': {'id': self.peer_group.pk}}), - content_type='application/json' + BGPPeerGroup.objects.bulk_create(peer_groups) + + +# # def test_graphql(self): +# # url = reverse('graphql') +# # query = 'query peer_group($id: Int!){peer_group(id: $id){name}}' +# # response = self.gql_client.post( +# # url, +# # json.dumps({'query': query, 'variables': {'id': self.peer_group.pk}}), +# # content_type='application/json' +# # ) +# # self.assertEqual(response.status_code, 200) +# # self.assertEqual(json.loads(response.content)['data']['peer_group']['name'], self.peer_group.name) + +# # def test_graphql_list(self): +# # url = reverse('graphql') +# # query = '{peer_group_list{name}}' +# # response = self.gql_client.post( +# # url, +# # json.dumps({'query': query}), +# # content_type='application/json' +# # ) +# # self.assertEqual(response.status_code, 200) + + +class BGPSessionAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = BGPSession + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id", "name", "url"] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + site = Site.objects.create(name="test", slug="test") + manufacturer = Manufacturer.objects.create(name="Juniper", slug="juniper") + device_role = DeviceRole.objects.create(name="Firewall", slug="firewall") + device_type = DeviceType.objects.create( + slug="srx3600", model="SRX3600", manufacturer=manufacturer ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(json.loads(response.content)['data']['peer_group']['name'], self.peer_group.name) - - def test_graphql_list(self): - url = reverse('graphql') - query = '{peer_group_list{name}}' - response = self.gql_client.post( - url, - json.dumps({'query': query}), - content_type='application/json' + device = Device.objects.create( + device_type=device_type, name="device1", role=device_role, site=site ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - - -class SessionTestCase(BaseTestCase): - def setUp(self): - super().setUp() - self.base_url_lookup = 'plugins-api:netbox_bgp-api:session' - site = Site.objects.create(name='test', slug='test') - manufacturer = Manufacturer.objects.create(name='Juniper', slug='juniper') - device_role = DeviceRole.objects.create(name='Firewall', slug='firewall') - device_type = DeviceType.objects.create(slug='srx3600', model='SRX3600', manufacturer=manufacturer) - self.device = Device.objects.create( - device_type=device_type, name='device1', device_role=device_role, site=site, + device2 = Device.objects.create( + device_type=device_type, name="device2", role=device_role, site=site ) - intf = Interface.objects.create(name='test_intf', device=self.device) - local_ip = IPAddress.objects.create(address='1.1.1.1/32') - remote_ip = IPAddress.objects.create(address='2.2.2.2/32') - self.local_ip = IPAddress.objects.create(address='3.3.3.3/32') - self.remote_ip = IPAddress.objects.create(address='4.4.4.4/32') + intf = Interface.objects.create(name="test_intf1", device=device) + intf2 = Interface.objects.create(name="test_intf2", device=device) + intf3 = Interface.objects.create(name="test_intf3", device=device) + local_ip = IPAddress.objects.create(address="1.1.1.1/32") + remote_ip1 = IPAddress.objects.create(address="2.2.2.2/32") + remote_ip2 = IPAddress.objects.create(address="3.3.3.3/32") + remote_ip3 = IPAddress.objects.create(address="4.4.4.4/32") intf.ip_addresses.add(local_ip) - self.device.save() - self.rir = RIR.objects.create(name="rir") - self.local_as = ASN.objects.create(asn=65000, rir=self.rir, description='local_as') - self.remote_as = ASN.objects.create(asn=65001, rir=self.rir, description='remote_as') - local_as = ASN.objects.create(asn=65002, rir=self.rir, description='local_as') - remote_as = ASN.objects.create(asn=65003, rir=self.rir, description='remote_as') - self.peer_group = BGPPeerGroup.objects.create(name='peer_group', description='peer_group_description') - self.session = BGPSession.objects.create( - name='session', - description='session_descr', - local_as=local_as, - remote_as=remote_as, - local_address=local_ip, - remote_address=remote_ip, - status='active', - peer_group=self.peer_group, - comments="comment_session_test" + rir = RIR.objects.create(name="rir") + local_as = ASN.objects.create(asn=65002, rir=rir, description="local_as") + remote_as = ASN.objects.create(asn=65003, rir=rir, description="remote_as") + peer_group = BGPPeerGroup.objects.create( + name="peer_group", description="peer_group_description" + ) + rp = RoutingPolicy.objects.create( + name="rp1", description="test_rp", comments="comments_routing_policy" + ) + pl1 = PrefixList.objects.create( + name="pl1", + description="test_pl", + family=IPAddressFamilyChoices.FAMILY_4, + comments="comments_pl", ) - def test_list_session(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_session(self): - url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.session.pk}) - response = self.client.get(url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['name'], self.session.name) - self.assertEqual(response.data['description'], self.session.description) - self.assertEqual(response.data['local_as']['asn'], self.session.local_as.asn) - self.assertEqual(response.data['remote_as']['asn'], self.session.remote_as.asn) - self.assertEqual(response.data['local_address']['address'], self.session.local_address.address) - self.assertEqual(response.data['remote_address']['address'], self.session.remote_address.address) - self.assertEqual(response.data['status']['value'], self.session.status) - self.assertEqual(response.data['peer_group']['name'], self.session.peer_group.name) - self.assertEqual(response.data['peer_group']['description'], self.session.peer_group.description) - self.assertEqual(response.data['comments'], self.session.comments) - - - def test_create_session(self): - url = reverse(f'{self.base_url_lookup}-list') - data = { - 'name': 'test_session', - 'description': 'session_descr', - 'local_as': self.local_as.pk, - 'remote_as': self.remote_as.pk, - 'local_address': self.local_ip.pk, - 'remote_address': self.remote_ip.pk, - 'status': 'active', - 'device': self.device.pk, - 'peer_group': self.peer_group.pk, - 'comments': 'comment_session_test1' - - } - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).name, 'test_session') - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).description, 'session_descr') - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).comments, 'comment_session_test1') - - - def test_update_session(self): - url = reverse(f'{self.base_url_lookup}-detail', kwargs={'pk': self.session.pk}) - data = {'description': 'new_description2', 'comments': 'comment_session_test2'} - - response = self.client.patch(url, data, format='json') - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_204_NO_CONTENT]) - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).description, 'new_description2') - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).comments, 'comment_session_test2') - - - def test_duplicate_session(self): - url = reverse(f'{self.base_url_lookup}-list') - data = { - 'name': 'test_session', - 'description': 'session_descr', - 'local_as': self.local_as.pk, - 'remote_as': self.remote_as.pk, - 'local_address': self.local_ip.pk, - 'remote_address': self.remote_ip.pk, - 'status': 'active', - 'device': self.device.pk, - 'peer_group': self.peer_group.pk - - } - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_session_no_device(self): - url = reverse(f'{self.base_url_lookup}-list') - data = { - 'name': 'test_session', - 'description': 'session_descr', - 'local_as': self.local_as.pk, - 'remote_as': self.remote_as.pk, - 'local_address': self.local_ip.pk, - 'remote_address': self.remote_ip.pk, - 'status': 'active', - 'peer_group': self.peer_group.pk, - 'device': None - - } - response = self.client.post(url, data, format='json') - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).name, 'test_session') - self.assertEqual(BGPSession.objects.get(pk=response.data['id']).description, 'session_descr') - - def test_graphql(self): - url = reverse('graphql') - query = 'query bgp_session($id: Int!){bgp_session(id: $id){name}}' - response = self.gql_client.post( - url, - json.dumps({'query': query, 'variables': {'id': self.session.pk}}), - content_type='application/json' + sessions = ( + BGPSession( + name="Session1", + description="Session1", + comments="Session1", + local_as=local_as, + remote_as=remote_as, + local_address=local_ip, + remote_address=remote_ip1, + peer_group=peer_group, + status=SessionStatusChoices.STATUS_ACTIVE, + ), + BGPSession( + name="Session2", + description="Session2", + comments="Session2", + local_as=local_as, + remote_as=remote_as, + local_address=local_ip, + remote_address=remote_ip1, + peer_group=peer_group, + status=SessionStatusChoices.STATUS_ACTIVE, + ), + BGPSession( + name="Session3", + description="Session3", + comments="Session3", + local_as=local_as, + remote_as=remote_as, + local_address=local_ip, + remote_address=remote_ip1, + peer_group=peer_group, + status=SessionStatusChoices.STATUS_ACTIVE, + prefix_list_in=pl1, + ), + ) + BGPSession.objects.bulk_create(sessions) + + # sessions[0].import_policies.add(rp) + + cls.create_data = [ + { + "name": "test_session1", + "description": "test_session1", + "comments": "test_session1", + "local_as": local_as.id, + "remote_as": remote_as.id, + "device": device2.id, + "local_address": local_ip.id, + "remote_address": remote_ip1.id, + "export_policies": [rp.id], + }, + { + "name": "test_session2", + "description": "test_session2", + "comments": "test_session2", + "local_as": local_as.id, + "remote_as": remote_as.id, + "device": device2.id, + "local_address": local_ip.id, + "remote_address": remote_ip2.id, + "import_policies": [rp.id], + }, + { + "name": "test_session3", + "description": "test_session3", + "comments": "test_session3", + "local_as": local_as.id, + "remote_as": remote_as.id, + "device": device2.id, + "local_address": local_ip.id, + "remote_address": remote_ip3.id, + "prefix_list_in": pl1.id, + }, + ] + + +# # def test_graphql(self): +# # url = reverse('graphql') +# # query = 'query bgp_session($id: Int!){bgp_session(id: $id){name}}' +# # response = self.gql_client.post( +# # url, +# # json.dumps({'query': query, 'variables': {'id': self.session.pk}}), +# # content_type='application/json' +# # ) +# # self.assertEqual(response.status_code, 200) +# # self.assertEqual(json.loads(response.content)['data']['bgp_session']['name'], self.session.name) + +# # def test_graphql_list(self): +# # url = reverse('graphql') +# # query = '{bgp_session_list{name}}' +# # response = self.gql_client.post( +# # url, +# # json.dumps({'query': query}), +# # content_type='application/json' +# # ) +# # self.assertEqual(response.status_code, 200) + + +class RoutingPolicyAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = RoutingPolicy + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id", "name", "url"] + + create_data = [ + { + "name": "test_routing_policy", + "description": "routing_policy_desc", + "comments": "routing_policy_comment1", + }, + { + "name": "test_routing_policy2", + "description": "routing_policy_desc2", + "comments": "routing_policy_comment2", + }, + { + "name": "test_routing_policy3", + "description": "routing_policy_desc3", + "comments": "routing_policy_comment3", + }, + ] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + routing_policies = ( + RoutingPolicy( + name="Route-map 1", description="Route-map 1", comments="Route-map 1" + ), + RoutingPolicy( + name="Route-map 2", description="Route-map 2", comments="Route-map 2" + ), + RoutingPolicy( + name="Route-map 3", description="Route-map 3", comments="Route-map 3" + ), + ) + RoutingPolicy.objects.bulk_create(routing_policies) + + +class RoutingPolicyRuleAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = RoutingPolicyRule + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id"] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + rp1 = RoutingPolicy.objects.create( + name="rp1", description="test_rp1", comments="comments_routing_policy1" + ) + rp2 = RoutingPolicy.objects.create( + name="rp2", description="test_rp2", comments="comments_routing_policy2" + ) + pl1 = PrefixList.objects.create( + name="pl1", description="test_pl", family=IPAddressFamilyChoices.FAMILY_4 + ) + pl2 = PrefixList.objects.create( + name="pl1", description="test_pl", family=IPAddressFamilyChoices.FAMILY_6 + ) + com1 = Community.objects.create(value="65000:65000") + com_list1 = CommunityList.objects.create( + name="community list 1", description="community list 1" + ) + + routing_policies_rules = ( + RoutingPolicyRule( + routing_policy=rp1, + index=10, + action=ActionChoices._choices[0][0], + description="Rule1", + ), + RoutingPolicyRule( + routing_policy=rp1, + index=20, + action=ActionChoices._choices[0][0], + description="Rule2", + ), + RoutingPolicyRule( + routing_policy=rp1, + index=30, + action=ActionChoices._choices[0][1], + description="Rule3", + ), + ) + RoutingPolicyRule.objects.bulk_create(routing_policies_rules) + + cls.create_data = [ + { + "routing_policy": rp2.id, + "description": "rule4", + "index": 10, + "action": "permit", + "comments": "rule4", + "match_ip_address": [pl1.id], + }, + { + "routing_policy": rp2.id, + "description": "rule5", + "index": 20, + "action": "permit", + "comments": "rule5", + "match_ipv6_address": [pl2.id], + "set_actions": "{'set': 'origin incomplete'", + }, + { + "routing_policy": rp2.id, + "description": "rule6", + "index": 30, + "action": "deny", + "comments": "rule6", + "match_community": [com1.id], + }, + { + "routing_policy": rp2.id, + "description": "rule7", + "index": 40, + "action": "deny", + "comments": "rule6", + "match_community_list": [com_list1.id], + }, + ] + + +class PrefixListAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = PrefixList + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id", "name", "url"] + + create_data = [ + { + "name": "test_prefix_list", + "description": "prefix_list_desc", + "comments": "prefix_list_comment1", + "family": "ipv4", + }, + { + "name": "test_prefix_list2", + "description": "prefix_list_desc2", + "comments": "prefix_list_comment2", + "family": "ipv4", + }, + { + "name": "test_prefix_list3", + "description": "prefix_list_desc3", + "comments": "prefix_list_comment3", + "family": "ipv6", + }, + ] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + + prefix_lists = ( + PrefixList( + name="prefix_list 1", + description="prefix_list 1", + comments="prefix_list 1", + family=IPAddressFamilyChoices.FAMILY_4, + ), + PrefixList( + name="prefix_list 2", + description="prefix_list 2", + comments="prefix_list 2", + family=IPAddressFamilyChoices.FAMILY_6, + ), + PrefixList( + name="prefix_list 3", + description="prefix_list 3", + comments="prefix_list 3", + family=IPAddressFamilyChoices.FAMILY_4, + ), + ) + PrefixList.objects.bulk_create(prefix_lists) + + +class PrefixListRuleAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + # APIViewTestCases.DeleteObjectViewTestCase, 405 != 204 : Expected HTTP status 204; received 405: {'detail': ErrorDetail(string='Method "DELETE" not allowed.' +): + model = PrefixListRule + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id"] + + # bulk_update_data = { + # "description": "Test Community desc", + # } # 405 != 200 : Expected HTTP status 200; received 405: {'detail': ErrorDetail(string='Method "PATCH" not allowed.' + + @classmethod + def setUpTestData(cls): + pl1 = PrefixList.objects.create( + name="pl1", description="test_pl1", family=IPAddressFamilyChoices.FAMILY_4 + ) + pl2 = PrefixList.objects.create( + name="pl2", description="test_pl2", family=IPAddressFamilyChoices.FAMILY_4 ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(json.loads(response.content)['data']['bgp_session']['name'], self.session.name) - - def test_graphql_list(self): - url = reverse('graphql') - query = '{bgp_session_list{name}}' - response = self.gql_client.post( - url, - json.dumps({'query': query}), - content_type='application/json' + subnet1 = Prefix.objects.create(prefix="10.0.0.0/24") + subnet2 = Prefix.objects.create(prefix="10.0.0.0/24") + + prefix_list_rules = ( + PrefixListRule( + prefix_list=pl1, + index=10, + action=ActionChoices._choices[0][0], + prefix=subnet1, + ge=24, + le=32, + RoutingPolicyRule="pl_rule_1", + comments="pl_rule_1", + ), + PrefixListRule( + prefix_list=pl1, + index=20, + action=ActionChoices._choices[0][0], + prefix=subnet2, + ge=24, + le=32, + description="pl_rule_2", + comments="pl_rule_2", + ), + PrefixListRule( + prefix_list=pl1, + index=30, + action=ActionChoices._choices[0][1], + prefix_custom="0.0.0.0/0", + ge=8, + le=32, + description="pl_rule_3", + comments="pl_rule_3", + ), ) - 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', comments='comments_routing_policy') - - 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) - self.assertEqual(response.data['comments'], self.rp.comments) - - def test_create_routing_policy(self): - url = reverse(f'{self.base_url_lookup}-list') - data = {'name': 'testrp', 'description': 'test_rp1', 'comments': 'comment_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') - self.assertEqual(RoutingPolicy.objects.get(pk=response.data['id']).comments, 'comment_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', comments='comments_pl') - - 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) - self.assertEqual(response.data['comments'], self.obj.comments) - - def test_create_prefix_list(self): - url = reverse(f'{self.base_url_lookup}-list') - data = {'name': 'testrp', 'description': 'test_rp1', 'family': 'ipv4', 'comments': 'comment_rp1'} - 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') - self.assertEqual(PrefixList.objects.get(pk=response.data['id']).comments, 'comment_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) + PrefixListRule.objects.bulk_create(prefix_list_rules) + + cls.create_data = [ + { + "prefix_list": pl2.id, + "description": "rule4", + "index": 10, + "action": "permit", + "comments": "rule4", + "prefix": subnet1.id, + "ge": 25, + "le": 32, + }, + { + "prefix_list": pl2.id, + "description": "rule5", + "index": 20, + "action": "permit", + "comments": "rule5", + "prefix": subnet2.id, + "ge": 26, + "le": 32, + }, + { + "prefix_list": pl2.id, + "description": "rule6", + "index": 30, + "action": "deny", + "comments": "rule6", + "prefix_custom": "0.0.0.0/0", + }, + ] + + +# class TestAPISchema(APITestCase): +# 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, 200) From f9229ccd36a1568b044b4b75f971b31d56c5d14c Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 8 Apr 2024 07:09:26 +0000 Subject: [PATCH 15/44] add familly of Prefixlist on UI --- netbox_bgp/templates/netbox_bgp/prefixlist.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/netbox_bgp/templates/netbox_bgp/prefixlist.html b/netbox_bgp/templates/netbox_bgp/prefixlist.html index d3a3ce1..6c626df 100644 --- a/netbox_bgp/templates/netbox_bgp/prefixlist.html +++ b/netbox_bgp/templates/netbox_bgp/prefixlist.html @@ -52,9 +52,13 @@
- + + + + + 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 %} {% endblock %} -{% block controls %} -
- {% if perms.netbox_bgp.change_session %} - - Edit - - {% endif %} - {% if perms.netbox_bgp.delete_session %} - - Delete - - {% endif %} -
-{% endblock controls %} + {% block tabs %}
nameName {{ object.name }}
Family{{ object.family }}
Description {{ object.description|placeholder }}
- + diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index 3bd97ae..b5d9411 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -30,6 +30,7 @@ # Community List Rules path('community-list-rule/', views.CommunityListRuleListView.as_view(), name='communitylistrule_list'), path('community-list-rule/add/', views.CommunityListRuleEditView.as_view(), name='communitylistrule_add'), + path('community-list-rule/delete/', views.CommunityListRuleBulkDeleteView.as_view(), name='communitylistrule_bulk_delete'), path('community-list-rule//', views.CommunityListRuleView.as_view(), name='communitylistrule'), path('community-list-rule//edit/', views.CommunityListRuleEditView.as_view(), name='communitylistrule_edit'), path('community-list-rule//delete/', views.CommunityListRuleDeleteView.as_view(), name='communitylistrule_delete'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 61ff557..78a443e 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -202,7 +202,7 @@ class RoutingPolicyListView(generic.ObjectListView): filterset = filtersets.RoutingPolicyFilterSet filterset_form = forms.RoutingPolicyFilterForm table = tables.RoutingPolicyTable - actions = {'add': {'add'}} + actions = {'add': {'add'}, 'bulk_delete': {'delete'}} class RoutingPolicyEditView(generic.ObjectEditView): From 6996e293c88f12259aa5d10b26ab40f7875ddb6e Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 19 Apr 2024 06:02:26 +0000 Subject: [PATCH 28/44] remove overloaded buttons for prefix list + add bulk_delete button --- netbox_bgp/templates/netbox_bgp/prefixlist.html | 14 ++------------ netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 4 ++-- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/netbox_bgp/templates/netbox_bgp/prefixlist.html b/netbox_bgp/templates/netbox_bgp/prefixlist.html index 6c626df..017b751 100644 --- a/netbox_bgp/templates/netbox_bgp/prefixlist.html +++ b/netbox_bgp/templates/netbox_bgp/prefixlist.html @@ -8,25 +8,15 @@ {% block breadcrumbs %} {% endblock %} -{% block controls %} +{% block extra_controls %}
{% if perms.netbox_bgp.change_prefixlist %} Rule {% endif %} - {% if perms.netbox_bgp.change_routingpolicy %} - - Edit - - {% endif %} - {% if perms.netbox_bgp.delete_routingpolicy %} - - Delete - - {% endif %}
-{% endblock controls %} +{% endblock extra_controls %} {% block tabs %}
nameName {{ object.name }}
- + diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index 9c6b217..43bf1b7 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -22,6 +22,7 @@ # Community Lists path('community-list/', views.CommunityListListView.as_view(), name='communitylist_list'), path('community-list/add/', views.CommunityListEditView.as_view(), name='communitylist_add'), + path('community-list/import/', views.CommunityListBulkImportView.as_view(), name='communitylist_import'), path('community-list/delete/', views.CommunityListBulkDeleteView.as_view(), name='communitylist_bulk_delete'), path('community-list//', views.CommListView.as_view(), name='communitylist'), path('community-list//edit/', views.CommunityListEditView.as_view(), name='communitylist_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index afa204f..9c77a05 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -64,7 +64,7 @@ class CommunityListListView(generic.ObjectListView): filterset = filtersets.CommunityListFilterSet filterset_form = forms.CommunityListFilterForm table = tables.CommunityListTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}} + actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class CommunityListEditView(generic.ObjectEditView): @@ -96,6 +96,10 @@ class CommunityListDeleteView(generic.ObjectDeleteView): queryset = CommunityList.objects.all() default_return_url = 'plugins:netbox_bgp:communitylist_list' +class CommunityListBulkImportView(generic.BulkImportView): + queryset = CommunityList.objects.all() + model_form = forms.CommunityListImportForm + # Community List Rule From a707ed84e4640699f116290d6d4d286c2349f6b7 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sun, 28 Apr 2024 13:20:05 +0000 Subject: [PATCH 34/44] bulk_import for routing policy --- netbox_bgp/forms.py | 7 +++++++ netbox_bgp/navigation.py | 6 ++++++ netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 6 +++++- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index ba7e72a..feb466b 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -477,6 +477,13 @@ class Meta: fields = ["name", "description", "tags", "comments"] +class RoutingPolicyImportForm(NetBoxModelImportForm): + + class Meta: + model = RoutingPolicy + fields = ("name", "description", "tags") + + class BGPPeerGroupFilterForm(NetBoxModelFilterSetForm): model = BGPPeerGroup q = forms.CharField(required=False, label="Search") diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index 2039c09..ee0606b 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -72,6 +72,12 @@ icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_routingpolicy'], ), + PluginMenuButton( + link='plugins:netbox_bgp:routingpolicy_import', + title='Import', + icon_class='mdi mdi-upload', + permissions=['netbox_bgp.add_routingpolicy'], + ), ), ), PluginMenuItem( diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index 43bf1b7..cbc966e 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -49,6 +49,7 @@ # Routing Policies path('routing-policy/', views.RoutingPolicyListView.as_view(), name='routingpolicy_list'), path('routing-policy/add/', views.RoutingPolicyEditView.as_view(), name='routingpolicy_add'), + path('routing-policy/import/', views.RoutingPolicyBulkImportView.as_view(), name='routingpolicy_import'), path('routing-policy/delete/', views.RoutingPolicyBulkDeleteView.as_view(), name='routingpolicy_bulk_delete'), path('routing-policy//', views.RoutingPolicyView.as_view(), name='routingpolicy'), path('routing-policy//edit/', views.RoutingPolicyEditView.as_view(), name='routingpolicy_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 9c77a05..600f8eb 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -206,7 +206,7 @@ class RoutingPolicyListView(generic.ObjectListView): filterset = filtersets.RoutingPolicyFilterSet filterset_form = forms.RoutingPolicyFilterForm table = tables.RoutingPolicyTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}} + actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class RoutingPolicyEditView(generic.ObjectEditView): @@ -244,6 +244,10 @@ class RoutingPolicyDeleteView(generic.ObjectDeleteView): queryset = RoutingPolicy.objects.all() default_return_url = 'plugins:netbox_bgp:routingpolicy_list' +class RoutingPolicyBulkImportView(generic.BulkImportView): + queryset = RoutingPolicy.objects.all() + model_form = forms.RoutingPolicyImportForm + # Routing Policy Rule From b6f54d41cdcbb116624fe942be4175f587432ac1 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sun, 28 Apr 2024 13:45:42 +0000 Subject: [PATCH 35/44] bulk_import for peer group + add policies on session import form --- netbox_bgp/forms.py | 33 +++++++++++++++++++++++++++++++++ netbox_bgp/navigation.py | 6 ++++++ netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 6 +++++- 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index feb466b..9a4522f 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -14,6 +14,7 @@ from utilities.forms.fields import ( DynamicModelChoiceField, CSVModelChoiceField, + CSVModelMultipleChoiceField, DynamicModelMultipleChoiceField, TagFilterField, CSVChoiceField, @@ -306,6 +307,18 @@ class BGPSessionImportForm(NetBoxModelImportForm): to_field_name="name", help_text=_("Peer Group"), ) + import_policies = CSVModelMultipleChoiceField( + queryset=RoutingPolicy.objects.all(), + to_field_name="name", + required=False, + help_text=_("Import policies name"), + ) + export_policies = CSVModelMultipleChoiceField( + queryset=RoutingPolicy.objects.all(), + to_field_name="name", + required=False, + help_text=_("Export policies name"), + ) prefix_list_in = CSVModelChoiceField( queryset=PrefixList.objects.all(), required=False, @@ -329,6 +342,8 @@ class Meta: "tenant", "status", "peer_group", + "import_policies", + "export_policies", "local_address", "remote_address", "local_as", @@ -515,6 +530,24 @@ class Meta: "comments", ] +class BGPPeerGroupImportForm(NetBoxModelImportForm): + + import_policies = CSVModelMultipleChoiceField( + queryset=RoutingPolicy.objects.all(), + to_field_name="name", + required=False, + help_text=_("Import policies name"), + ) + export_policies = CSVModelMultipleChoiceField( + queryset=RoutingPolicy.objects.all(), + to_field_name="name", + required=False, + help_text=_("Export policies name"), + ) + + class Meta: + model = BGPPeerGroup + fields = ("name", "description", "import_policies", "export_policies", "tags") class RoutingPolicyRuleForm(NetBoxModelForm): continue_entry = forms.IntegerField( diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index ee0606b..fe89cbf 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -104,6 +104,12 @@ icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_bgppeergroup'], ), + PluginMenuButton( + link='plugins:netbox_bgp:bgppeergroup_import', + title='Import', + icon_class='mdi mdi-upload', + permissions=['netbox_bgp.add_bgppeergroup'], + ), ), ) ) diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index cbc966e..78f8b9c 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -58,6 +58,7 @@ # Peer Groups path('peer-group/', views.BGPPeerGroupListView.as_view(), name='bgppeergroup_list'), path('peer-group/add/', views.BGPPeerGroupEditView.as_view(), name='bgppeergroup_add'), + path('peer-group/import/', views.BGPPeerGroupBulkImportView.as_view(), name='bgppeergroup_import'), path('peer-group/delete/', views.BGPPeerGroupBulkDeleteView.as_view(), name='bgppeergroup_bulk_delete'), path('peer-group//', views.BGPPeerGroupView.as_view(), name='bgppeergroup'), path('peer-group//edit/', views.BGPPeerGroupEditView.as_view(), name='bgppeergroup_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 600f8eb..fb6821d 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -300,7 +300,7 @@ class BGPPeerGroupListView(generic.ObjectListView): filterset = filtersets.BGPPeerGroupFilterSet filterset_form = forms.BGPPeerGroupFilterForm table = tables.BGPPeerGroupTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}} + actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class BGPPeerGroupEditView(generic.ObjectEditView): @@ -341,6 +341,10 @@ class BGPPeerGroupDeleteView(generic.ObjectDeleteView): queryset = BGPPeerGroup.objects.all() default_return_url = 'plugins:netbox_bgp:bgppeergroup_list' +class BGPPeerGroupBulkImportView(generic.BulkImportView): + queryset = BGPPeerGroup.objects.all() + model_form = forms.BGPPeerGroupImportForm + # Prefix List From b2499659bca2e9f4c757dd02cf44842ebc7e336e Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 29 Apr 2024 08:19:54 +0000 Subject: [PATCH 36/44] bulk_import for prefix list --- netbox_bgp/forms.py | 18 +++++++++++++++++- netbox_bgp/navigation.py | 6 ++++++ netbox_bgp/tables.py | 3 ++- netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 5 ++++- 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index 9a4522f..d819eb7 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -40,7 +40,11 @@ CommunityListRule, ) -from .choices import SessionStatusChoices, CommunityStatusChoices +from .choices import ( + SessionStatusChoices, + CommunityStatusChoices, + IPAddressFamilyChoices, +) class CommunityForm(NetBoxModelForm): @@ -530,6 +534,7 @@ class Meta: "comments", ] + class BGPPeerGroupImportForm(NetBoxModelImportForm): import_policies = CSVModelMultipleChoiceField( @@ -549,6 +554,7 @@ class Meta: model = BGPPeerGroup fields = ("name", "description", "import_policies", "export_policies", "tags") + class RoutingPolicyRuleForm(NetBoxModelForm): continue_entry = forms.IntegerField( required=False, @@ -629,6 +635,16 @@ class Meta: fields = ["name", "description", "family", "tags", "comments"] +class PrefixListImportForm(NetBoxModelImportForm): + family = CSVChoiceField( + choices=IPAddressFamilyChoices, required=True, help_text=_("Family address") + ) + + class Meta: + model = PrefixList + fields = ("name", "description", "family", "tags") + + class PrefixListRuleForm(NetBoxModelForm): prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), diff --git a/netbox_bgp/navigation.py b/netbox_bgp/navigation.py index fe89cbf..dcf7999 100644 --- a/netbox_bgp/navigation.py +++ b/netbox_bgp/navigation.py @@ -91,6 +91,12 @@ icon_class='mdi mdi-plus-thick', permissions=['netbox_bgp.add_prefixlist'], ), + PluginMenuButton( + link='plugins:netbox_bgp:prefixlist_import', + title='Import', + icon_class='mdi mdi-upload', + permissions=['netbox_bgp.add_prefixlist'], + ), ), ), PluginMenuItem( diff --git a/netbox_bgp/tables.py b/netbox_bgp/tables.py index 668ac67..4c986cb 100644 --- a/netbox_bgp/tables.py +++ b/netbox_bgp/tables.py @@ -158,10 +158,11 @@ class Meta(NetBoxTable.Meta): class PrefixListTable(NetBoxTable): name = tables.LinkColumn() + family = ChoiceFieldColumn() class Meta(NetBoxTable.Meta): model = PrefixList - fields = ('pk', 'name', 'description') + fields = ('pk', 'name', 'description', 'family') class PrefixListRuleTable(NetBoxTable): diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index 78f8b9c..271c2b5 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -75,6 +75,7 @@ # Prefix Lists path('prefix-list/', views.PrefixListListView.as_view(), name='prefixlist_list'), path('prefix-list/add/', views.PrefixListEditView.as_view(), name='prefixlist_add'), + path('prefix-list/import/', views.PrefixListBulkImportView.as_view(), name='prefixlist_import'), path('prefix-list/delete/', views.PrefixListBulkDeleteView.as_view(), name='prefixlist_bulk_delete'), path('prefix-list//', views.PrefixListView.as_view(), name='prefixlist'), path('prefix-list//edit/', views.PrefixListEditView.as_view(), name='prefixlist_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index fb6821d..4eccfa4 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -354,7 +354,7 @@ class PrefixListListView(generic.ObjectListView): filterset = filtersets.PrefixListFilterSet filterset_form = forms.PrefixListFilterForm table = tables.PrefixListTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}} + actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class PrefixListEditView(generic.ObjectEditView): @@ -390,6 +390,9 @@ class PrefixListDeleteView(generic.ObjectDeleteView): queryset = PrefixList.objects.all() default_return_url = 'plugins:netbox_bgp:prefixlist_list' +class PrefixListBulkImportView(generic.BulkImportView): + queryset = PrefixList.objects.all() + model_form = forms.PrefixListImportForm # Prefix List Rule From c42b90b2e5ac69ff7f6a673bea57f62cc3ee3c1b Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Mon, 29 Apr 2024 08:36:50 +0000 Subject: [PATCH 37/44] bulk edit for community list --- netbox_bgp/forms.py | 12 ++++++++++++ netbox_bgp/tables.py | 2 +- netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 7 ++++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index d819eb7..2029a38 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -125,6 +125,18 @@ class Meta: fields = ["name", "description", "tags", "comments"] +class CommunityListBulkEditForm(NetBoxModelBulkEditForm): + pk = forms.ModelMultipleChoiceField( + queryset=CommunityList.objects.all(), widget=forms.MultipleHiddenInput + ) + description = forms.CharField(max_length=200, required=False) + + model = CommunityList + nullable_fields = [ + "description", + ] + + class CommunityListImportForm(NetBoxModelImportForm): class Meta: diff --git a/netbox_bgp/tables.py b/netbox_bgp/tables.py index 4c986cb..08c5978 100644 --- a/netbox_bgp/tables.py +++ b/netbox_bgp/tables.py @@ -55,7 +55,7 @@ class CommunityListTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = CommunityList - fields = ('pk', 'name', 'description') + fields = ('pk', 'name', 'description', 'actions') class CommunityListRuleTable(NetBoxTable): diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index 271c2b5..c963b18 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -23,6 +23,7 @@ path('community-list/', views.CommunityListListView.as_view(), name='communitylist_list'), path('community-list/add/', views.CommunityListEditView.as_view(), name='communitylist_add'), path('community-list/import/', views.CommunityListBulkImportView.as_view(), name='communitylist_import'), + path('community-list/edit/', views.CommunityListBulkEditView.as_view(), name='communitylist_bulk_edit'), path('community-list/delete/', views.CommunityListBulkDeleteView.as_view(), name='communitylist_bulk_delete'), path('community-list//', views.CommListView.as_view(), name='communitylist'), path('community-list//edit/', views.CommunityListEditView.as_view(), name='communitylist_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 4eccfa4..46d3796 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -64,7 +64,6 @@ class CommunityListListView(generic.ObjectListView): filterset = filtersets.CommunityListFilterSet filterset_form = forms.CommunityListFilterForm table = tables.CommunityListTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class CommunityListEditView(generic.ObjectEditView): @@ -76,6 +75,12 @@ class CommunityListBulkDeleteView(generic.BulkDeleteView): queryset = CommunityList.objects.all() table = tables.CommunityListTable +class CommunityListBulkEditView(generic.BulkEditView): + queryset = CommunityList.objects.all() + filterset = filtersets.CommunityListFilterSet + table = tables.CommunityListTable + form = forms.CommunityListBulkEditForm + class CommListView(generic.ObjectView): queryset = CommunityList.objects.all() From a5c840e671b881cc8fc2e8cb7d12a42207230875 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 3 May 2024 07:41:23 +0000 Subject: [PATCH 38/44] bulkedit for routing policy + remove pk from form as it comes from parent class --- netbox_bgp/forms.py | 15 +++++++++------ netbox_bgp/tables.py | 2 +- netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 7 ++++++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index 2029a38..71049d6 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -75,9 +75,6 @@ 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) status = forms.ChoiceField( @@ -126,9 +123,6 @@ class Meta: class CommunityListBulkEditForm(NetBoxModelBulkEditForm): - pk = forms.ModelMultipleChoiceField( - queryset=CommunityList.objects.all(), widget=forms.MultipleHiddenInput - ) description = forms.CharField(max_length=200, required=False) model = CommunityList @@ -515,6 +509,15 @@ class Meta: fields = ("name", "description", "tags") +class RoutingPolicyBulkEditForm(NetBoxModelBulkEditForm): + description = forms.CharField(max_length=200, required=False) + + model = RoutingPolicy + nullable_fields = [ + "description", + ] + + class BGPPeerGroupFilterForm(NetBoxModelFilterSetForm): model = BGPPeerGroup q = forms.CharField(required=False, label="Search") diff --git a/netbox_bgp/tables.py b/netbox_bgp/tables.py index 08c5978..bc0b2a6 100644 --- a/netbox_bgp/tables.py +++ b/netbox_bgp/tables.py @@ -111,7 +111,7 @@ class RoutingPolicyTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = RoutingPolicy - fields = ('pk', 'name', 'description') + fields = ('pk', 'name', 'description', 'actions') class BGPPeerGroupTable(NetBoxTable): diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index c963b18..b00c729 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -51,6 +51,7 @@ path('routing-policy/', views.RoutingPolicyListView.as_view(), name='routingpolicy_list'), path('routing-policy/add/', views.RoutingPolicyEditView.as_view(), name='routingpolicy_add'), path('routing-policy/import/', views.RoutingPolicyBulkImportView.as_view(), name='routingpolicy_import'), + path('routing-policy/edit/', views.RoutingPolicyBulkEditView.as_view(), name='routingpolicy_bulk_edit'), path('routing-policy/delete/', views.RoutingPolicyBulkDeleteView.as_view(), name='routingpolicy_bulk_delete'), path('routing-policy//', views.RoutingPolicyView.as_view(), name='routingpolicy'), path('routing-policy//edit/', views.RoutingPolicyEditView.as_view(), name='routingpolicy_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 46d3796..90bf2eb 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -211,7 +211,6 @@ class RoutingPolicyListView(generic.ObjectListView): filterset = filtersets.RoutingPolicyFilterSet filterset_form = forms.RoutingPolicyFilterForm table = tables.RoutingPolicyTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class RoutingPolicyEditView(generic.ObjectEditView): @@ -223,6 +222,12 @@ class RoutingPolicyBulkDeleteView(generic.BulkDeleteView): queryset = RoutingPolicy.objects.all() table = tables.RoutingPolicyTable +class RoutingPolicyBulkEditView(generic.BulkEditView): + queryset = RoutingPolicy.objects.all() + filterset = filtersets.RoutingPolicyFilterSet + table = tables.RoutingPolicyTable + form = forms.RoutingPolicyBulkEditForm + class RoutingPolicyView(generic.ObjectView): queryset = RoutingPolicy.objects.all() From 8a288e293b76ebd7f80451acaadd3c2ed93bb802 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 3 May 2024 07:49:54 +0000 Subject: [PATCH 39/44] bulkedit for prefix list --- netbox_bgp/forms.py | 15 +++++++++++++++ netbox_bgp/tables.py | 2 +- netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 7 ++++++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index 71049d6..8c11c10 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -660,6 +660,21 @@ class Meta: fields = ("name", "description", "family", "tags") +class PrefixListBulkEditForm(NetBoxModelBulkEditForm): + description = forms.CharField(max_length=200, required=False) + + family = forms.ChoiceField( + label=_("Family"), + required=False, + choices=IPAddressFamilyChoices, + ) + + model = PrefixList + nullable_fields = [ + "description", + ] + + class PrefixListRuleForm(NetBoxModelForm): prefix = DynamicModelChoiceField( queryset=Prefix.objects.all(), diff --git a/netbox_bgp/tables.py b/netbox_bgp/tables.py index bc0b2a6..20502ed 100644 --- a/netbox_bgp/tables.py +++ b/netbox_bgp/tables.py @@ -162,7 +162,7 @@ class PrefixListTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = PrefixList - fields = ('pk', 'name', 'description', 'family') + fields = ('pk', 'name', 'description', 'family', 'actions') class PrefixListRuleTable(NetBoxTable): diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index b00c729..6f34ead 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -78,6 +78,7 @@ path('prefix-list/', views.PrefixListListView.as_view(), name='prefixlist_list'), path('prefix-list/add/', views.PrefixListEditView.as_view(), name='prefixlist_add'), path('prefix-list/import/', views.PrefixListBulkImportView.as_view(), name='prefixlist_import'), + path('prefix-list/edit/', views.PrefixListBulkEditView.as_view(), name='prefixlist_bulk_edit'), path('prefix-list/delete/', views.PrefixListBulkDeleteView.as_view(), name='prefixlist_bulk_delete'), path('prefix-list//', views.PrefixListView.as_view(), name='prefixlist'), path('prefix-list//edit/', views.PrefixListEditView.as_view(), name='prefixlist_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index 90bf2eb..b861a44 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -364,7 +364,6 @@ class PrefixListListView(generic.ObjectListView): filterset = filtersets.PrefixListFilterSet filterset_form = forms.PrefixListFilterForm table = tables.PrefixListTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class PrefixListEditView(generic.ObjectEditView): @@ -376,6 +375,12 @@ class PrefixListBulkDeleteView(generic.BulkDeleteView): queryset = PrefixList.objects.all() table = tables.PrefixListTable +class PrefixListBulkEditView(generic.BulkEditView): + queryset = PrefixList.objects.all() + filterset = filtersets.PrefixListFilterSet + table = tables.PrefixListTable + form = forms.PrefixListBulkEditForm + class PrefixListView(generic.ObjectView): queryset = PrefixList.objects.all() From 75a48a7ba64af622cd666d37c7673ba1360f5412 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Fri, 3 May 2024 07:57:50 +0000 Subject: [PATCH 40/44] bulkedit for peer groups --- netbox_bgp/forms.py | 20 ++++++++++++++++++++ netbox_bgp/tables.py | 2 +- netbox_bgp/urls.py | 1 + netbox_bgp/views.py | 7 ++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/netbox_bgp/forms.py b/netbox_bgp/forms.py index 8c11c10..10801fa 100644 --- a/netbox_bgp/forms.py +++ b/netbox_bgp/forms.py @@ -570,6 +570,26 @@ class Meta: fields = ("name", "description", "import_policies", "export_policies", "tags") +class BGPPeerGroupBulkEditForm(NetBoxModelBulkEditForm): + description = forms.CharField(max_length=200, required=False) + + import_policies = DynamicModelMultipleChoiceField( + queryset=RoutingPolicy.objects.all(), + required=False, + 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/"), + ) + + model = BGPPeerGroup + nullable_fields = [ + "description", "import_policies", "export_policies" + ] + + class RoutingPolicyRuleForm(NetBoxModelForm): continue_entry = forms.IntegerField( required=False, diff --git a/netbox_bgp/tables.py b/netbox_bgp/tables.py index 20502ed..b46f6c9 100644 --- a/netbox_bgp/tables.py +++ b/netbox_bgp/tables.py @@ -132,7 +132,7 @@ class Meta(NetBoxTable.Meta): model = BGPPeerGroup fields = ( 'pk', 'name', 'description', 'tags', - 'import_policies', 'export_policies' + 'import_policies', 'export_policies', 'actions' ) default_columns = ( 'pk', 'name', 'description' diff --git a/netbox_bgp/urls.py b/netbox_bgp/urls.py index 6f34ead..c8819ce 100644 --- a/netbox_bgp/urls.py +++ b/netbox_bgp/urls.py @@ -61,6 +61,7 @@ path('peer-group/', views.BGPPeerGroupListView.as_view(), name='bgppeergroup_list'), path('peer-group/add/', views.BGPPeerGroupEditView.as_view(), name='bgppeergroup_add'), path('peer-group/import/', views.BGPPeerGroupBulkImportView.as_view(), name='bgppeergroup_import'), + path('peer-group/edit/', views.BGPPeerGroupBulkEditView.as_view(), name='bgppeergroup_bulk_edit'), path('peer-group/delete/', views.BGPPeerGroupBulkDeleteView.as_view(), name='bgppeergroup_bulk_delete'), path('peer-group//', views.BGPPeerGroupView.as_view(), name='bgppeergroup'), path('peer-group//edit/', views.BGPPeerGroupEditView.as_view(), name='bgppeergroup_edit'), diff --git a/netbox_bgp/views.py b/netbox_bgp/views.py index b861a44..da7e274 100644 --- a/netbox_bgp/views.py +++ b/netbox_bgp/views.py @@ -310,7 +310,6 @@ class BGPPeerGroupListView(generic.ObjectListView): filterset = filtersets.BGPPeerGroupFilterSet filterset_form = forms.BGPPeerGroupFilterForm table = tables.BGPPeerGroupTable - actions = {'add': {'add'}, 'bulk_delete': {'delete'}, 'import': {'add'},} class BGPPeerGroupEditView(generic.ObjectEditView): @@ -355,6 +354,12 @@ class BGPPeerGroupBulkImportView(generic.BulkImportView): queryset = BGPPeerGroup.objects.all() model_form = forms.BGPPeerGroupImportForm +class BGPPeerGroupBulkEditView(generic.BulkEditView): + queryset = BGPPeerGroup.objects.all() + filterset = filtersets.BGPPeerGroupFilterSet + table = tables.BGPPeerGroupTable + form = forms.BGPPeerGroupBulkEditForm + # Prefix List From e419b4aa0596cb0485656ed00bc5367805a6e815 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 4 May 2024 13:11:09 +0000 Subject: [PATCH 41/44] test --- netbox_bgp/choices.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox_bgp/choices.py b/netbox_bgp/choices.py index 8fbc60d..fd4fd94 100644 --- a/netbox_bgp/choices.py +++ b/netbox_bgp/choices.py @@ -78,6 +78,7 @@ class IPAddressFamilyChoices(ChoiceSet): FAMILY_4 = 'ipv4' FAMILY_6 = 'ipv6' + CHOICES = ( (FAMILY_4, 'IPv4'), From 87ecb2d290639d6137faf98c7b57c84b749e900e Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Sat, 4 May 2024 14:19:40 +0000 Subject: [PATCH 42/44] add test_api for communitylist + communitylistrules --- netbox_bgp/api/serializers.py | 2 + netbox_bgp/choices.py | 1 - netbox_bgp/tests/test_api.py | 121 ++++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 8 deletions(-) diff --git a/netbox_bgp/api/serializers.py b/netbox_bgp/api/serializers.py index e3886af..4402b7e 100644 --- a/netbox_bgp/api/serializers.py +++ b/netbox_bgp/api/serializers.py @@ -239,6 +239,7 @@ class Meta: "tags", "custom_fields", "display", + "description", "community_list", "created", "last_updated", @@ -246,6 +247,7 @@ class Meta: "community", "comments", ] + brief_fields = ("id", "display", "description") class RoutingPolicyRuleSerializer(NetBoxModelSerializer): diff --git a/netbox_bgp/choices.py b/netbox_bgp/choices.py index fd4fd94..d804e52 100644 --- a/netbox_bgp/choices.py +++ b/netbox_bgp/choices.py @@ -79,7 +79,6 @@ class IPAddressFamilyChoices(ChoiceSet): FAMILY_4 = 'ipv4' FAMILY_6 = 'ipv6' - CHOICES = ( (FAMILY_4, 'IPv4'), (FAMILY_6, 'IPv6'), diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index b12e1ac..3926707 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -10,6 +10,7 @@ from netbox_bgp.models import ( Community, CommunityList, + CommunityListRule, BGPPeerGroup, BGPSession, RoutingPolicy, @@ -56,6 +57,115 @@ def setUpTestData(cls): ) Community.objects.bulk_create(communities) + +class CommunityListAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + APIViewTestCases.DeleteObjectViewTestCase, + # APIViewTestCases.GraphQLTestCase, +): + model = CommunityList + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id", "name", "url"] + + create_data = [ + {"name": "CL1", "description": "cl1_api"}, + {"name": "CL2", "description": "cl2_api"}, + {"name": "CL3", "description": "cl3_api"}, + ] + + bulk_update_data = { + "description": "Test Community List desc", + } + + @classmethod + def setUpTestData(cls): + communitylists = ( + CommunityList(name="CL4", description="cl4"), + CommunityList(name="CL5", description="cl5"), + CommunityList(name="CL6", description="cl6"), + ) + CommunityList.objects.bulk_create(communitylists) + + +class CommunityListRuleAPITestCase( + APIViewTestCases.GetObjectViewTestCase, + APIViewTestCases.ListObjectsViewTestCase, + APIViewTestCases.CreateObjectViewTestCase, + APIViewTestCases.UpdateObjectViewTestCase, + APIViewTestCases.DeleteObjectViewTestCase, + # APIViewTestCases.GraphQLTestCase, +): + model = CommunityListRule + view_namespace = "plugins-api:netbox_bgp" + brief_fields = ["description", "display", "id"] + + bulk_update_data = { + "description": "Test Community List rules desc", + "action": "deny", + } + + @classmethod + def setUpTestData(cls): + com_list1 = CommunityList.objects.create( + name="community list 1", description="community list 1" + ) + com_list2 = CommunityList.objects.create( + name="community list 2", description="community list 2" + ) + com1 = Community.objects.create(value="65001:65004", description="community1") + com2 = Community.objects.create(value="65002:65005", description="community2") + com3 = Community.objects.create(value="65003:65006", description="community3") + + communitylistrules = ( + CommunityListRule( + community_list=com_list1, + community=com1, + action=ActionChoices._choices[0][0], + description="rule1", + ), + CommunityListRule( + community_list=com_list1, + community=com2, + action=ActionChoices._choices[0][0], + description="rule2", + ), + CommunityListRule( + community_list=com_list1, + community=com3, + action=ActionChoices._choices[0][1], + description="rule3", + ), + ) + CommunityListRule.objects.bulk_create(communitylistrules) + + cls.create_data = [ + { + "community_list": com_list2.id, + "description": "rule4", + "community": com1.id, + "action": "permit", + "comments": "rule4", + }, + { + "community_list": com_list2.id, + "description": "rule5", + "community": com2.id, + "action": "permit", + "comments": "rule5", + }, + { + "community_list": com_list2.id, + "description": "rule6", + "community": com3.id, + "action": "deny", + "comments": "rule6", + }, + ] + + class BGPPeerGroupAPITestCase( APIViewTestCases.GetObjectViewTestCase, APIViewTestCases.ListObjectsViewTestCase, @@ -269,7 +379,7 @@ class RoutingPolicyAPITestCase( bulk_update_data = { "description": "Test Routing policy desc", - } + } @classmethod def setUpTestData(cls): @@ -301,7 +411,7 @@ class RoutingPolicyRuleAPITestCase( bulk_update_data = { "description": "Test Routing policy rules desc", - "action": "deny" + "action": "deny", } @classmethod @@ -417,7 +527,7 @@ class PrefixListAPITestCase( bulk_update_data = { "description": "Test Prefix list desc", - } + } @classmethod def setUpTestData(cls): @@ -457,10 +567,7 @@ class PrefixListRuleAPITestCase( view_namespace = "plugins-api:netbox_bgp" brief_fields = ["description", "display", "id"] - bulk_update_data = { - "description": "Test Prefix list rules desc", - "action": "deny" - } + bulk_update_data = {"description": "Test Prefix list rules desc", "action": "deny"} @classmethod def setUpTestData(cls): From 89615889a73851f5dce75d09b988d24bdfb74794 Mon Sep 17 00:00:00 2001 From: pl0xym0r <148605740+pl0xym0r@users.noreply.github.com> Date: Thu, 9 May 2024 12:20:15 +0000 Subject: [PATCH 43/44] go to netbox 4.0 --- netbox_bgp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox_bgp/__init__.py b/netbox_bgp/__init__.py index f3645dc..d1c7bdf 100644 --- a/netbox_bgp/__init__.py +++ b/netbox_bgp/__init__.py @@ -11,7 +11,7 @@ class BGPConfig(PluginConfig): author_email = 'mgk.kolek@gmail.com' base_url = 'bgp' required_settings = [] - min_version = '4.0.0-dev' + min_version = '4.0.0' max_version = '4.0.99' default_settings = { 'device_ext_page': 'right', From 82e1a7692b5732abb90ba086b9f7f5b9d285aaba 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: Wed, 15 May 2024 18:35:08 +0500 Subject: [PATCH 44/44] fix for some test in testapi --- Makefile | 4 ++-- netbox_bgp/tests/test_api.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index b8cbc36..48aecd9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -PYTHON_VER?=3.8 -NETBOX_VER?=v3.7.0 +PYTHON_VER?=3.10 +NETBOX_VER?=v4.0.2 NAME=netbox-bgp diff --git a/netbox_bgp/tests/test_api.py b/netbox_bgp/tests/test_api.py index f240d92..51f1036 100644 --- a/netbox_bgp/tests/test_api.py +++ b/netbox_bgp/tests/test_api.py @@ -269,9 +269,6 @@ def setUpTestData(cls): family=IPAddressFamilyChoices.FAMILY_4, comments="comments_pl", ) - self.session.save() - self.session.import_policies.add(import_pol) - self.session.export_policies.add(export_pol) sessions = ( BGPSession(
nameName {{ object.name }}