Skip to content

Commit c1d422e

Browse files
authored
Merge pull request #101 from k01ek/develop
Develop
2 parents c8ab3f9 + c67f125 commit c1d422e

23 files changed

+977
-183
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VER?=3.8
2-
NETBOX_VER?=v3.2.0
2+
NETBOX_VER?=v3.2.4
33

44
NAME=netbox-bgp
55

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# NetBox BGP Plugin
22
[Netbox](https://github.com/netbox-community/netbox) plugin for BGP related objects documentation.
33

4+
## Features
5+
This plugin provide following Models:
6+
* AS Numbers (will be removed in 0.8.0)
7+
* BGP Communities
8+
* BGP Sessions
9+
* Routing Policy
10+
* Prefix Lists (new in 0.7.0)
11+
412
## Compatibility
513

614
| | |

netbox_bgp/api/serializers.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from netbox_bgp.models import (
1313
ASN, ASNStatusChoices, BGPSession, SessionStatusChoices, RoutingPolicy, BGPPeerGroup,
14-
Community, RoutingPolicyRule
14+
Community, RoutingPolicyRule, PrefixList, PrefixListRule
1515
)
1616

1717

@@ -161,6 +161,7 @@ def to_representation(self, instance):
161161
)
162162
return ret
163163

164+
164165
class NestedBGPSessionSerializer(WritableNestedSerializer):
165166
url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:bgpsession')
166167

@@ -169,6 +170,7 @@ class Meta:
169170
fields = ['id', 'url', 'name', 'description']
170171
validators = []
171172

173+
172174
class CommunitySerializer(NetBoxModelSerializer):
173175
status = ChoiceField(choices=ASNStatusChoices, required=False)
174176
tenant = NestedTenantSerializer(required=False, allow_null=True)
@@ -183,3 +185,15 @@ class RoutingPolicyRuleSerializer(NetBoxModelSerializer):
183185
class Meta:
184186
model = RoutingPolicyRule
185187
fields = '__all__'
188+
189+
190+
class PrefixListSerializer(NetBoxModelSerializer):
191+
class Meta:
192+
model = PrefixList
193+
fields = '__all__'
194+
195+
196+
class PrefixListRuleSerializer(NetBoxModelSerializer):
197+
class Meta:
198+
model = PrefixListRule
199+
fields = '__all__'

netbox_bgp/api/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .views import (
44
ASNViewSet, BGPSessionViewSet, RoutingPolicyViewSet, BGPPeerGroupViewSet,
5-
CommunityViewSet
5+
CommunityViewSet, PrefixListViewSet
66
)
77

88
router = routers.DefaultRouter()
@@ -13,6 +13,7 @@
1313
router.register('peer-group', BGPPeerGroupViewSet, 'peergroup')
1414
router.register('bgppeergroup', BGPPeerGroupViewSet, 'bgppeergroup')
1515
router.register('community', CommunityViewSet)
16+
router.register('prefix-list', PrefixListViewSet)
1617

1718

1819
urlpatterns = router.urls

netbox_bgp/api/views.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
from .serializers import (
44
ASNSerializer, BGPSessionSerializer, RoutingPolicySerializer, BGPPeerGroupSerializer,
5-
CommunitySerializer
5+
CommunitySerializer, PrefixListSerializer
66
)
7-
from netbox_bgp.models import ASN, BGPSession, RoutingPolicy, BGPPeerGroup, Community
7+
from netbox_bgp.models import ASN, BGPSession, RoutingPolicy, BGPPeerGroup, Community, PrefixList
88
from netbox_bgp.filters import (
99
ASNFilterSet, BGPSessionFilterSet, RoutingPolicyFilterSet, BGPPeerGroupFilterSet,
10-
CommunityFilterSet
10+
CommunityFilterSet, PrefixListFilterSet
1111
)
1212

1313

@@ -39,3 +39,9 @@ class CommunityViewSet(ModelViewSet):
3939
queryset = Community.objects.all()
4040
serializer_class = CommunitySerializer
4141
filterset_class = CommunityFilterSet
42+
43+
44+
class PrefixListViewSet(ModelViewSet):
45+
queryset = PrefixList.objects.all()
46+
serializer_class = PrefixListSerializer
47+
filterset_class = PrefixListFilterSet

netbox_bgp/choices.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from utilities.choices import ChoiceSet
2+
3+
4+
class ASNStatusChoices(ChoiceSet):
5+
6+
STATUS_ACTIVE = 'active'
7+
STATUS_RESERVED = 'reserved'
8+
STATUS_DEPRECATED = 'deprecated'
9+
10+
CHOICES = (
11+
(STATUS_ACTIVE, 'Active', 'blue'),
12+
(STATUS_RESERVED, 'Reserved', 'cyan'),
13+
(STATUS_DEPRECATED, 'Deprecated', 'red'),
14+
)
15+
16+
17+
class SessionStatusChoices(ChoiceSet):
18+
19+
STATUS_OFFLINE = 'offline'
20+
STATUS_ACTIVE = 'active'
21+
STATUS_PLANNED = 'planned'
22+
STATUS_FAILED = 'failed'
23+
24+
CHOICES = (
25+
(STATUS_OFFLINE, 'Offline', 'orange'),
26+
(STATUS_ACTIVE, 'Active', 'green'),
27+
(STATUS_PLANNED, 'Planned', 'cyan'),
28+
(STATUS_FAILED, 'Failed', 'red'),
29+
)
30+
31+
32+
class ActionChoices(ChoiceSet):
33+
34+
CHOICES = [
35+
('permit', 'Permit', 'green'),
36+
('deny', 'Deny', 'red'),
37+
]
38+
39+
40+
class AFISAFIChoices(ChoiceSet):
41+
AFISAFI_IPV4_UNICAST = 'ipv4-unicast'
42+
AFISAFI_IPV4_MULTICAST = 'ipv4-multicast'
43+
AFISAFI_IPV4_FLOWSPEC = 'ipv4-flowspec'
44+
45+
AFISAFI_IPV6_UNICAST = 'ipv6-unicast'
46+
AFISAFI_IPV6_MULTICAST = 'ipv6-multicast'
47+
AFISAFI_IPV6_FLOWSPEC = 'ipv6-flowspec'
48+
49+
AFISAFI_L2VPN_VPLS = 'l2vpn-vpls'
50+
AFISAFI_L2VPN_EVPN = 'l2vpn-evpn'
51+
52+
AFISAFI_VPNV4_UNICAST = 'vpnv4-unicast'
53+
AFISAFI_VPNV4_MULTICAST = 'vpnv4-multicast'
54+
AFISAFI_VPNV4_FLOWSPEC = 'vpnv4-flowspec'
55+
56+
AFISAFI_VPNV6_UNICAST = 'vpnv6-unicast'
57+
AFISAFI_VPNV6_MULTICAST = 'vpnv6-multicast'
58+
AFISAFI_VPNV6_FLOWSPEC = 'vpnv6-flowspec'
59+
60+
CHOICES = (
61+
62+
)
63+
64+
65+
class IPAddressFamilyChoices(ChoiceSet):
66+
67+
FAMILY_4 = 4
68+
FAMILY_6 = 6
69+
70+
CHOICES = (
71+
(FAMILY_4, 'IPv4'),
72+
(FAMILY_6, 'IPv6'),
73+
)

netbox_bgp/filters.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from netaddr.core import AddrFormatError
55
from extras.filters import TagFilter
66

7-
from .models import ASN, Community, BGPSession, RoutingPolicy, BGPPeerGroup
7+
from .models import ASN, Community, BGPSession, RoutingPolicy, BGPPeerGroup, PrefixList
88
from ipam.models import IPAddress
99
from dcim.models import Device
1010

@@ -217,3 +217,25 @@ def search(self, queryset, name, value):
217217
| Q(description__icontains=value)
218218
)
219219
return queryset.filter(qs_filter)
220+
221+
222+
class PrefixListFilterSet(django_filters.FilterSet):
223+
q = django_filters.CharFilter(
224+
method='search',
225+
label='Search',
226+
)
227+
tag = TagFilter()
228+
229+
class Meta:
230+
model = PrefixList
231+
fields = ['name', 'description']
232+
233+
def search(self, queryset, name, value):
234+
"""Perform the filtered search."""
235+
if not value.strip():
236+
return queryset
237+
qs_filter = (
238+
Q(name__icontains=value)
239+
| Q(description__icontains=value)
240+
)
241+
return queryset.filter(qs_filter)

netbox_bgp/forms.py

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
from .models import (
2121
ASN, ASNStatusChoices, Community, BGPSession,
22-
SessionStatusChoices, RoutingPolicy, BGPPeerGroup, RoutingPolicyRule
22+
SessionStatusChoices, RoutingPolicy, BGPPeerGroup,
23+
RoutingPolicyRule, PrefixList, PrefixListRule
24+
2325
)
2426

2527

@@ -421,7 +423,7 @@ class RoutingPolicyForm(NetBoxModelForm):
421423

422424
class Meta:
423425
model = RoutingPolicy
424-
fields = ['name', 'description']
426+
fields = ['name', 'description', 'tags']
425427

426428

427429
class BGPPeerGroupFilterForm(NetBoxModelFilterSetForm):
@@ -460,19 +462,24 @@ class Meta:
460462

461463

462464
class RoutingPolicyRuleForm(NetBoxModelForm):
465+
continue_entry = forms.IntegerField(
466+
required=False,
467+
label='Continue',
468+
help_text='Null for disable, 0 to next entry, or any sequence number'
469+
)
463470
match_community = DynamicModelMultipleChoiceField(
464471
queryset=Community.objects.all(),
465472
required=False,
466473
)
467-
match_ip = DynamicModelMultipleChoiceField(
468-
queryset=Prefix.objects.all(),
474+
match_ip_address = DynamicModelMultipleChoiceField(
475+
queryset=PrefixList.objects.all(),
469476
required=False,
470-
label='Match Prefix',
477+
label='Match IP address Prefix lists',
471478
)
472-
match_ip_cond = forms.JSONField(
473-
label='Match filtered prefixes',
474-
help_text='Filter for Prefixes, e.g., {"site__name": "site1", "tenant__name": "tenant1"}',
479+
match_ipv6_address = DynamicModelMultipleChoiceField(
480+
queryset=PrefixList.objects.all(),
475481
required=False,
482+
label='Match IPv6 address Prefix lists',
476483
)
477484
match_custom = forms.JSONField(
478485
label='Custom Match',
@@ -488,7 +495,57 @@ class RoutingPolicyRuleForm(NetBoxModelForm):
488495
class Meta:
489496
model = RoutingPolicyRule
490497
fields = [
491-
'routing_policy', 'index', 'action', 'match_community',
492-
'match_ip', 'match_ip_cond', 'match_custom',
498+
'routing_policy', 'index', 'action', 'continue_entry', 'match_community',
499+
'match_ip_address', 'match_ipv6_address', 'match_custom',
493500
'set_actions', 'description',
494501
]
502+
503+
504+
class PrefixListFilterForm(NetBoxModelFilterSetForm):
505+
model = PrefixList
506+
q = forms.CharField(
507+
required=False,
508+
label='Search'
509+
)
510+
511+
tag = TagFilterField(model)
512+
513+
514+
class PrefixListForm(NetBoxModelForm):
515+
tags = DynamicModelMultipleChoiceField(
516+
queryset=Tag.objects.all(),
517+
required=False
518+
)
519+
520+
class Meta:
521+
model = PrefixList
522+
fields = ['name', 'description', 'tags']
523+
524+
525+
class PrefixListRuleForm(NetBoxModelForm):
526+
prefix = DynamicModelChoiceField(
527+
queryset=Prefix.objects.all(),
528+
required=False,
529+
help_text='NetBox Prefix Object',
530+
)
531+
prefix_custom = IPNetworkFormField(
532+
required=False,
533+
label='Prefix',
534+
help_text='Just IP field for define special prefix like 0.0.0.0/0',
535+
)
536+
ge = forms.IntegerField(
537+
label='Greater than or equal to',
538+
required=False,
539+
)
540+
le = forms.IntegerField(
541+
label='Less than or equal to',
542+
required=False,
543+
)
544+
545+
class Meta:
546+
model = PrefixListRule
547+
fields = [
548+
'prefix_list', 'index',
549+
'action', 'prefix', 'prefix_custom',
550+
'ge', 'le'
551+
]
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Generated by Django 4.0.4 on 2022-08-18 13:47
2+
3+
import django.core.serializers.json
4+
import django.core.validators
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
import ipam.fields
8+
import taggit.managers
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
dependencies = [
14+
('extras', '0073_journalentry_tags_custom_fields'),
15+
('ipam', '0057_created_datetimefield'),
16+
('netbox_bgp', '0022_netbox_bgp'),
17+
]
18+
19+
operations = [
20+
migrations.CreateModel(
21+
name='PrefixList',
22+
fields=[
23+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
24+
('created', models.DateTimeField(auto_now_add=True, null=True)),
25+
('last_updated', models.DateTimeField(auto_now=True, null=True)),
26+
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
27+
('name', models.CharField(max_length=100)),
28+
('description', models.CharField(blank=True, max_length=200)),
29+
('family', models.CharField(max_length=10)),
30+
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
31+
],
32+
options={
33+
'verbose_name_plural': 'Prefix Lists',
34+
'unique_together': {('name', 'description', 'family')},
35+
},
36+
),
37+
migrations.CreateModel(
38+
name='PrefixListRule',
39+
fields=[
40+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
41+
('created', models.DateTimeField(auto_now_add=True, null=True)),
42+
('last_updated', models.DateTimeField(auto_now=True, null=True)),
43+
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
44+
('index', models.PositiveIntegerField()),
45+
('action', models.CharField(max_length=30)),
46+
('prefix_custom', ipam.fields.IPNetworkField(blank=True, null=True)),
47+
('ge', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])),
48+
('le', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])),
49+
('prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='ipam.prefix')),
50+
('prefix_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prefrules', to='netbox_bgp.prefixlist')),
51+
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
52+
],
53+
options={
54+
'ordering': ('prefix_list', 'index'),
55+
'unique_together': {('prefix_list', 'index')},
56+
},
57+
),
58+
]

0 commit comments

Comments
 (0)