Skip to content

Commit

Permalink
Merge pull request #101 from k01ek/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
k01ek authored Aug 19, 2022
2 parents c8ab3f9 + c67f125 commit c1d422e
Show file tree
Hide file tree
Showing 23 changed files with 977 additions and 183 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PYTHON_VER?=3.8
NETBOX_VER?=v3.2.0
NETBOX_VER?=v3.2.4

NAME=netbox-bgp

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# NetBox BGP Plugin
[Netbox](https://github.com/netbox-community/netbox) plugin for BGP related objects documentation.

## Features
This plugin provide following Models:
* AS Numbers (will be removed in 0.8.0)
* BGP Communities
* BGP Sessions
* Routing Policy
* Prefix Lists (new in 0.7.0)

## Compatibility

| | |
Expand Down
16 changes: 15 additions & 1 deletion netbox_bgp/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from netbox_bgp.models import (
ASN, ASNStatusChoices, BGPSession, SessionStatusChoices, RoutingPolicy, BGPPeerGroup,
Community, RoutingPolicyRule
Community, RoutingPolicyRule, PrefixList, PrefixListRule
)


Expand Down Expand Up @@ -161,6 +161,7 @@ def to_representation(self, instance):
)
return ret


class NestedBGPSessionSerializer(WritableNestedSerializer):
url = HyperlinkedIdentityField(view_name='plugins:netbox_bgp:bgpsession')

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


class CommunitySerializer(NetBoxModelSerializer):
status = ChoiceField(choices=ASNStatusChoices, required=False)
tenant = NestedTenantSerializer(required=False, allow_null=True)
Expand All @@ -183,3 +185,15 @@ class RoutingPolicyRuleSerializer(NetBoxModelSerializer):
class Meta:
model = RoutingPolicyRule
fields = '__all__'


class PrefixListSerializer(NetBoxModelSerializer):
class Meta:
model = PrefixList
fields = '__all__'


class PrefixListRuleSerializer(NetBoxModelSerializer):
class Meta:
model = PrefixListRule
fields = '__all__'
3 changes: 2 additions & 1 deletion netbox_bgp/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .views import (
ASNViewSet, BGPSessionViewSet, RoutingPolicyViewSet, BGPPeerGroupViewSet,
CommunityViewSet
CommunityViewSet, PrefixListViewSet
)

router = routers.DefaultRouter()
Expand All @@ -13,6 +13,7 @@
router.register('peer-group', BGPPeerGroupViewSet, 'peergroup')
router.register('bgppeergroup', BGPPeerGroupViewSet, 'bgppeergroup')
router.register('community', CommunityViewSet)
router.register('prefix-list', PrefixListViewSet)


urlpatterns = router.urls
12 changes: 9 additions & 3 deletions netbox_bgp/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from .serializers import (
ASNSerializer, BGPSessionSerializer, RoutingPolicySerializer, BGPPeerGroupSerializer,
CommunitySerializer
CommunitySerializer, PrefixListSerializer
)
from netbox_bgp.models import ASN, BGPSession, RoutingPolicy, BGPPeerGroup, Community
from netbox_bgp.models import ASN, BGPSession, RoutingPolicy, BGPPeerGroup, Community, PrefixList
from netbox_bgp.filters import (
ASNFilterSet, BGPSessionFilterSet, RoutingPolicyFilterSet, BGPPeerGroupFilterSet,
CommunityFilterSet
CommunityFilterSet, PrefixListFilterSet
)


Expand Down Expand Up @@ -39,3 +39,9 @@ class CommunityViewSet(ModelViewSet):
queryset = Community.objects.all()
serializer_class = CommunitySerializer
filterset_class = CommunityFilterSet


class PrefixListViewSet(ModelViewSet):
queryset = PrefixList.objects.all()
serializer_class = PrefixListSerializer
filterset_class = PrefixListFilterSet
73 changes: 73 additions & 0 deletions netbox_bgp/choices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from utilities.choices import ChoiceSet


class ASNStatusChoices(ChoiceSet):

STATUS_ACTIVE = 'active'
STATUS_RESERVED = 'reserved'
STATUS_DEPRECATED = 'deprecated'

CHOICES = (
(STATUS_ACTIVE, 'Active', 'blue'),
(STATUS_RESERVED, 'Reserved', 'cyan'),
(STATUS_DEPRECATED, 'Deprecated', 'red'),
)


class SessionStatusChoices(ChoiceSet):

STATUS_OFFLINE = 'offline'
STATUS_ACTIVE = 'active'
STATUS_PLANNED = 'planned'
STATUS_FAILED = 'failed'

CHOICES = (
(STATUS_OFFLINE, 'Offline', 'orange'),
(STATUS_ACTIVE, 'Active', 'green'),
(STATUS_PLANNED, 'Planned', 'cyan'),
(STATUS_FAILED, 'Failed', 'red'),
)


class ActionChoices(ChoiceSet):

CHOICES = [
('permit', 'Permit', 'green'),
('deny', 'Deny', 'red'),
]


class AFISAFIChoices(ChoiceSet):
AFISAFI_IPV4_UNICAST = 'ipv4-unicast'
AFISAFI_IPV4_MULTICAST = 'ipv4-multicast'
AFISAFI_IPV4_FLOWSPEC = 'ipv4-flowspec'

AFISAFI_IPV6_UNICAST = 'ipv6-unicast'
AFISAFI_IPV6_MULTICAST = 'ipv6-multicast'
AFISAFI_IPV6_FLOWSPEC = 'ipv6-flowspec'

AFISAFI_L2VPN_VPLS = 'l2vpn-vpls'
AFISAFI_L2VPN_EVPN = 'l2vpn-evpn'

AFISAFI_VPNV4_UNICAST = 'vpnv4-unicast'
AFISAFI_VPNV4_MULTICAST = 'vpnv4-multicast'
AFISAFI_VPNV4_FLOWSPEC = 'vpnv4-flowspec'

AFISAFI_VPNV6_UNICAST = 'vpnv6-unicast'
AFISAFI_VPNV6_MULTICAST = 'vpnv6-multicast'
AFISAFI_VPNV6_FLOWSPEC = 'vpnv6-flowspec'

CHOICES = (

)


class IPAddressFamilyChoices(ChoiceSet):

FAMILY_4 = 4
FAMILY_6 = 6

CHOICES = (
(FAMILY_4, 'IPv4'),
(FAMILY_6, 'IPv6'),
)
24 changes: 23 additions & 1 deletion netbox_bgp/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from netaddr.core import AddrFormatError
from extras.filters import TagFilter

from .models import ASN, Community, BGPSession, RoutingPolicy, BGPPeerGroup
from .models import ASN, Community, BGPSession, RoutingPolicy, BGPPeerGroup, PrefixList
from ipam.models import IPAddress
from dcim.models import Device

Expand Down Expand Up @@ -217,3 +217,25 @@ def search(self, queryset, name, value):
| Q(description__icontains=value)
)
return queryset.filter(qs_filter)


class PrefixListFilterSet(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
tag = TagFilter()

class Meta:
model = PrefixList
fields = ['name', 'description']

def search(self, queryset, name, value):
"""Perform the filtered search."""
if not value.strip():
return queryset
qs_filter = (
Q(name__icontains=value)
| Q(description__icontains=value)
)
return queryset.filter(qs_filter)
77 changes: 67 additions & 10 deletions netbox_bgp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

from .models import (
ASN, ASNStatusChoices, Community, BGPSession,
SessionStatusChoices, RoutingPolicy, BGPPeerGroup, RoutingPolicyRule
SessionStatusChoices, RoutingPolicy, BGPPeerGroup,
RoutingPolicyRule, PrefixList, PrefixListRule

)


Expand Down Expand Up @@ -421,7 +423,7 @@ class RoutingPolicyForm(NetBoxModelForm):

class Meta:
model = RoutingPolicy
fields = ['name', 'description']
fields = ['name', 'description', 'tags']


class BGPPeerGroupFilterForm(NetBoxModelFilterSetForm):
Expand Down Expand Up @@ -460,19 +462,24 @@ class Meta:


class RoutingPolicyRuleForm(NetBoxModelForm):
continue_entry = forms.IntegerField(
required=False,
label='Continue',
help_text='Null for disable, 0 to next entry, or any sequence number'
)
match_community = DynamicModelMultipleChoiceField(
queryset=Community.objects.all(),
required=False,
)
match_ip = DynamicModelMultipleChoiceField(
queryset=Prefix.objects.all(),
match_ip_address = DynamicModelMultipleChoiceField(
queryset=PrefixList.objects.all(),
required=False,
label='Match Prefix',
label='Match IP address Prefix lists',
)
match_ip_cond = forms.JSONField(
label='Match filtered prefixes',
help_text='Filter for Prefixes, e.g., {"site__name": "site1", "tenant__name": "tenant1"}',
match_ipv6_address = DynamicModelMultipleChoiceField(
queryset=PrefixList.objects.all(),
required=False,
label='Match IPv6 address Prefix lists',
)
match_custom = forms.JSONField(
label='Custom Match',
Expand All @@ -488,7 +495,57 @@ class RoutingPolicyRuleForm(NetBoxModelForm):
class Meta:
model = RoutingPolicyRule
fields = [
'routing_policy', 'index', 'action', 'match_community',
'match_ip', 'match_ip_cond', 'match_custom',
'routing_policy', 'index', 'action', 'continue_entry', 'match_community',
'match_ip_address', 'match_ipv6_address', 'match_custom',
'set_actions', 'description',
]


class PrefixListFilterForm(NetBoxModelFilterSetForm):
model = PrefixList
q = forms.CharField(
required=False,
label='Search'
)

tag = TagFilterField(model)


class PrefixListForm(NetBoxModelForm):
tags = DynamicModelMultipleChoiceField(
queryset=Tag.objects.all(),
required=False
)

class Meta:
model = PrefixList
fields = ['name', 'description', 'tags']


class PrefixListRuleForm(NetBoxModelForm):
prefix = DynamicModelChoiceField(
queryset=Prefix.objects.all(),
required=False,
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',
)
ge = forms.IntegerField(
label='Greater than or equal to',
required=False,
)
le = forms.IntegerField(
label='Less than or equal to',
required=False,
)

class Meta:
model = PrefixListRule
fields = [
'prefix_list', 'index',
'action', 'prefix', 'prefix_custom',
'ge', 'le'
]
58 changes: 58 additions & 0 deletions netbox_bgp/migrations/0023_netbox_bgp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Generated by Django 4.0.4 on 2022-08-18 13:47

import django.core.serializers.json
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import ipam.fields
import taggit.managers


class Migration(migrations.Migration):

dependencies = [
('extras', '0073_journalentry_tags_custom_fields'),
('ipam', '0057_created_datetimefield'),
('netbox_bgp', '0022_netbox_bgp'),
]

operations = [
migrations.CreateModel(
name='PrefixList',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
('name', models.CharField(max_length=100)),
('description', models.CharField(blank=True, max_length=200)),
('family', models.CharField(max_length=10)),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
],
options={
'verbose_name_plural': 'Prefix Lists',
'unique_together': {('name', 'description', 'family')},
},
),
migrations.CreateModel(
name='PrefixListRule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
('index', models.PositiveIntegerField()),
('action', models.CharField(max_length=30)),
('prefix_custom', ipam.fields.IPNetworkField(blank=True, null=True)),
('ge', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])),
('le', models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(128)])),
('prefix', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='ipam.prefix')),
('prefix_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prefrules', to='netbox_bgp.prefixlist')),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
],
options={
'ordering': ('prefix_list', 'index'),
'unique_together': {('prefix_list', 'index')},
},
),
]
Loading

0 comments on commit c1d422e

Please sign in to comment.