mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 18:08:38 -06:00
Introduce the RouteTarget model
This commit is contained in:
parent
9b16d6df2e
commit
dfb5a06d9d
@ -9,6 +9,7 @@ __all__ = [
|
|||||||
'NestedPrefixSerializer',
|
'NestedPrefixSerializer',
|
||||||
'NestedRIRSerializer',
|
'NestedRIRSerializer',
|
||||||
'NestedRoleSerializer',
|
'NestedRoleSerializer',
|
||||||
|
'NestedRouteTargetSerializer',
|
||||||
'NestedServiceSerializer',
|
'NestedServiceSerializer',
|
||||||
'NestedVLANGroupSerializer',
|
'NestedVLANGroupSerializer',
|
||||||
'NestedVLANSerializer',
|
'NestedVLANSerializer',
|
||||||
@ -29,6 +30,18 @@ class NestedVRFSerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'rd', 'display_name', 'prefix_count']
|
fields = ['id', 'url', 'name', 'rd', 'display_name', 'prefix_count']
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Route targets
|
||||||
|
#
|
||||||
|
|
||||||
|
class NestedRouteTargetSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.RouteTarget
|
||||||
|
fields = ['id', 'url', 'name']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs/aggregates
|
# RIRs/aggregates
|
||||||
#
|
#
|
||||||
|
@ -3,20 +3,17 @@ from collections import OrderedDict
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from drf_yasg.utils import swagger_serializer_method
|
from drf_yasg.utils import swagger_serializer_method
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.reverse import reverse
|
|
||||||
from rest_framework.validators import UniqueTogetherValidator
|
from rest_framework.validators import UniqueTogetherValidator
|
||||||
|
|
||||||
from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerializer
|
from dcim.api.nested_serializers import NestedDeviceSerializer, NestedSiteSerializer
|
||||||
from dcim.models import Interface
|
|
||||||
from extras.api.customfields import CustomFieldModelSerializer
|
from extras.api.customfields import CustomFieldModelSerializer
|
||||||
from extras.api.serializers import TaggedObjectSerializer
|
from extras.api.serializers import TaggedObjectSerializer
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS
|
from ipam.constants import IPADDRESS_ASSIGNMENT_MODELS
|
||||||
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
from tenancy.api.nested_serializers import NestedTenantSerializer
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||||
from utilities.api import (
|
from utilities.api import (
|
||||||
ChoiceField, ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer,
|
ChoiceField, ContentTypeField, SerializedPKRelatedField, ValidatedModelSerializer, get_serializer_for_model,
|
||||||
get_serializer_for_model,
|
|
||||||
)
|
)
|
||||||
from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
|
from virtualization.api.nested_serializers import NestedVirtualMachineSerializer
|
||||||
from .nested_serializers import *
|
from .nested_serializers import *
|
||||||
@ -40,6 +37,21 @@ class VRFSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Route targets
|
||||||
|
#
|
||||||
|
|
||||||
|
class RouteTargetSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='ipam-api:routetarget-detail')
|
||||||
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RouteTarget
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'name', 'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs/aggregates
|
# RIRs/aggregates
|
||||||
#
|
#
|
||||||
|
@ -8,6 +8,9 @@ router.APIRootView = views.IPAMRootView
|
|||||||
# VRFs
|
# VRFs
|
||||||
router.register('vrfs', views.VRFViewSet)
|
router.register('vrfs', views.VRFViewSet)
|
||||||
|
|
||||||
|
# Route targets
|
||||||
|
router.register('route-targets', views.RouteTargetViewSet)
|
||||||
|
|
||||||
# RIRs
|
# RIRs
|
||||||
router.register('rirs', views.RIRViewSet)
|
router.register('rirs', views.RIRViewSet)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from rest_framework.routers import APIRootView
|
|||||||
|
|
||||||
from extras.api.views import CustomFieldModelViewSet
|
from extras.api.views import CustomFieldModelViewSet
|
||||||
from ipam import filters
|
from ipam import filters
|
||||||
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
from utilities.api import ModelViewSet
|
from utilities.api import ModelViewSet
|
||||||
from utilities.constants import ADVISORY_LOCK_KEYS
|
from utilities.constants import ADVISORY_LOCK_KEYS
|
||||||
from utilities.utils import get_subquery
|
from utilities.utils import get_subquery
|
||||||
@ -38,6 +38,16 @@ class VRFViewSet(CustomFieldModelViewSet):
|
|||||||
filterset_class = filters.VRFFilterSet
|
filterset_class = filters.VRFFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Route targets
|
||||||
|
#
|
||||||
|
|
||||||
|
class RouteTargetViewSet(CustomFieldModelViewSet):
|
||||||
|
queryset = RouteTarget.objects.prefetch_related('tenant').prefetch_related('tags')
|
||||||
|
serializer_class = serializers.RouteTargetSerializer
|
||||||
|
filterset_class = filters.RouteTargetFilterSet
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs
|
# RIRs
|
||||||
#
|
#
|
||||||
|
@ -16,6 +16,7 @@ BGP_ASN_MAX = 2**32 - 1
|
|||||||
# * Type 1 (32-bit IPv4 address : 16-bit integer)
|
# * Type 1 (32-bit IPv4 address : 16-bit integer)
|
||||||
# * Type 2 (32-bit AS number : 16-bit integer)
|
# * Type 2 (32-bit AS number : 16-bit integer)
|
||||||
# 21 characters are sufficient to convey the longest possible string value (255.255.255.255:65535)
|
# 21 characters are sufficient to convey the longest possible string value (255.255.255.255:65535)
|
||||||
|
# Also used for RouteTargets
|
||||||
VRF_RD_MAX_LENGTH = 21
|
VRF_RD_MAX_LENGTH = 21
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from utilities.filters import (
|
|||||||
)
|
)
|
||||||
from virtualization.models import VirtualMachine, VMInterface
|
from virtualization.models import VirtualMachine, VMInterface
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -22,6 +22,7 @@ __all__ = (
|
|||||||
'PrefixFilterSet',
|
'PrefixFilterSet',
|
||||||
'RIRFilterSet',
|
'RIRFilterSet',
|
||||||
'RoleFilterSet',
|
'RoleFilterSet',
|
||||||
|
'RouteTargetFilterSet',
|
||||||
'ServiceFilterSet',
|
'ServiceFilterSet',
|
||||||
'VLANFilterSet',
|
'VLANFilterSet',
|
||||||
'VLANGroupFilterSet',
|
'VLANGroupFilterSet',
|
||||||
@ -50,6 +51,26 @@ class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Create
|
|||||||
fields = ['id', 'name', 'rd', 'enforce_unique']
|
fields = ['id', 'name', 'rd', 'enforce_unique']
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
|
q = django_filters.CharFilter(
|
||||||
|
method='search',
|
||||||
|
label='Search',
|
||||||
|
)
|
||||||
|
tag = TagFilter()
|
||||||
|
|
||||||
|
def search(self, queryset, name, value):
|
||||||
|
if not value.strip():
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(
|
||||||
|
Q(name__icontains=value) |
|
||||||
|
Q(description__icontains=value)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RouteTarget
|
||||||
|
fields = ['id', 'name']
|
||||||
|
|
||||||
|
|
||||||
class RIRFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
class RIRFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Rack, Region, Site
|
from dcim.models import Device, Interface, Rack, Region, Site
|
||||||
from extras.forms import (
|
from extras.forms import (
|
||||||
@ -16,7 +15,7 @@ from utilities.forms import (
|
|||||||
from virtualization.models import Cluster, VirtualMachine, VMInterface
|
from virtualization.models import Cluster, VirtualMachine, VMInterface
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
|
|
||||||
PREFIX_MASK_LENGTH_CHOICES = add_blank_choice([
|
PREFIX_MASK_LENGTH_CHOICES = add_blank_choice([
|
||||||
(i, i) for i in range(PREFIX_LENGTH_MIN, PREFIX_LENGTH_MAX + 1)
|
(i, i) for i in range(PREFIX_LENGTH_MIN, PREFIX_LENGTH_MAX + 1)
|
||||||
@ -98,6 +97,66 @@ class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Route targets
|
||||||
|
#
|
||||||
|
|
||||||
|
class RouteTargetForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
||||||
|
tags = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Tag.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RouteTarget
|
||||||
|
fields = [
|
||||||
|
'name', 'description', 'tenant_group', 'tenant', 'tags',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetCSVForm(CustomFieldModelCSVForm):
|
||||||
|
tenant = CSVModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Assigned tenant'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RouteTarget
|
||||||
|
fields = RouteTarget.csv_headers
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=RouteTarget.objects.all(),
|
||||||
|
widget=forms.MultipleHiddenInput()
|
||||||
|
)
|
||||||
|
tenant = DynamicModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
description = forms.CharField(
|
||||||
|
max_length=200,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = [
|
||||||
|
'tenant', 'description',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||||
|
model = RouteTarget
|
||||||
|
field_order = ['q', 'name', 'tenant_group', 'tenant']
|
||||||
|
q = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label='Search'
|
||||||
|
)
|
||||||
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs
|
# RIRs
|
||||||
#
|
#
|
||||||
|
34
netbox/ipam/migrations/0041_routetarget.py
Normal file
34
netbox/ipam/migrations/0041_routetarget.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 3.1 on 2020-09-24 15:19
|
||||||
|
|
||||||
|
import django.core.serializers.json
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tenancy', '0010_custom_field_data'),
|
||||||
|
('extras', '0052_delete_customfieldchoice_customfieldvalue'),
|
||||||
|
('ipam', '0040_service_drop_port'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RouteTarget',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||||
|
('created', models.DateField(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=21, unique=True)),
|
||||||
|
('description', models.CharField(blank=True, max_length=200)),
|
||||||
|
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||||
|
('tenant', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='route_targets', to='tenancy.tenant')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['name'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -107,6 +107,50 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
|
||||||
|
class RouteTarget(ChangeLoggedModel, CustomFieldModel):
|
||||||
|
"""
|
||||||
|
A BGP extended community used to control the redistribution of routes among VRFs, as defined in RFC 4364.
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=VRF_RD_MAX_LENGTH, # Same format options as VRF RD (RFC 4360 section 4)
|
||||||
|
unique=True,
|
||||||
|
help_text='Route target value (formatted in accordance with RFC 4360)'
|
||||||
|
)
|
||||||
|
description = models.CharField(
|
||||||
|
max_length=200,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
tenant = models.ForeignKey(
|
||||||
|
to='tenancy.Tenant',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='route_targets',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
|
objects = RestrictedQuerySet.as_manager()
|
||||||
|
|
||||||
|
csv_headers = ['name', 'description', 'tenant']
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['name']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('ipam:routetarget', args=[self.pk])
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return (
|
||||||
|
self.name,
|
||||||
|
self.description,
|
||||||
|
self.tenant.name if self.tenant else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RIR(ChangeLoggedModel):
|
class RIR(ChangeLoggedModel):
|
||||||
"""
|
"""
|
||||||
A Regional Internet Registry (RIR) is responsible for the allocation of a large portion of the global IP address
|
A Regional Internet Registry (RIR) is responsible for the allocation of a large portion of the global IP address
|
||||||
|
@ -5,7 +5,7 @@ from dcim.models import Interface
|
|||||||
from tenancy.tables import COL_TENANT
|
from tenancy.tables import COL_TENANT
|
||||||
from utilities.tables import BaseTable, BooleanColumn, ButtonsColumn, TagColumn, ToggleColumn
|
from utilities.tables import BaseTable, BooleanColumn, ButtonsColumn, TagColumn, ToggleColumn
|
||||||
from virtualization.models import VMInterface
|
from virtualization.models import VMInterface
|
||||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
|
|
||||||
RIR_UTILIZATION = """
|
RIR_UTILIZATION = """
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
@ -176,6 +176,26 @@ class VRFTable(BaseTable):
|
|||||||
default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
|
default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Route targets
|
||||||
|
#
|
||||||
|
|
||||||
|
class RouteTargetTable(BaseTable):
|
||||||
|
pk = ToggleColumn()
|
||||||
|
name = tables.LinkColumn()
|
||||||
|
tenant = tables.TemplateColumn(
|
||||||
|
template_code=COL_TENANT
|
||||||
|
)
|
||||||
|
tags = TagColumn(
|
||||||
|
url_name='ipam:vrf_list'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = RouteTarget
|
||||||
|
fields = ('pk', 'name', 'tenant', 'description', 'tags')
|
||||||
|
default_columns = ('pk', 'name', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs
|
# RIRs
|
||||||
#
|
#
|
||||||
|
@ -2,7 +2,7 @@ from django.urls import path
|
|||||||
|
|
||||||
from extras.views import ObjectChangeLogView
|
from extras.views import ObjectChangeLogView
|
||||||
from . import views
|
from . import views
|
||||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
|
|
||||||
app_name = 'ipam'
|
app_name = 'ipam'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -18,6 +18,17 @@ urlpatterns = [
|
|||||||
path('vrfs/<int:pk>/delete/', views.VRFDeleteView.as_view(), name='vrf_delete'),
|
path('vrfs/<int:pk>/delete/', views.VRFDeleteView.as_view(), name='vrf_delete'),
|
||||||
path('vrfs/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vrf_changelog', kwargs={'model': VRF}),
|
path('vrfs/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vrf_changelog', kwargs={'model': VRF}),
|
||||||
|
|
||||||
|
# Route targets
|
||||||
|
path('route-targets/', views.RouteTargetListView.as_view(), name='routetarget_list'),
|
||||||
|
path('route-targets/add/', views.RouteTargetEditView.as_view(), name='routetarget_add'),
|
||||||
|
path('route-targets/import/', views.RouteTargetBulkImportView.as_view(), name='routetarget_import'),
|
||||||
|
path('route-targets/edit/', views.RouteTargetBulkEditView.as_view(), name='routetarget_bulk_edit'),
|
||||||
|
path('route-targets/delete/', views.RouteTargetBulkDeleteView.as_view(), name='routetarget_bulk_delete'),
|
||||||
|
path('route-targets/<int:pk>/', views.RouteTargetView.as_view(), name='routetarget'),
|
||||||
|
path('route-targets/<int:pk>/edit/', views.RouteTargetEditView.as_view(), name='routetarget_edit'),
|
||||||
|
path('route-targets/<int:pk>/delete/', views.RouteTargetDeleteView.as_view(), name='routetarget_delete'),
|
||||||
|
path('route-targets/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='routetarget_changelog', kwargs={'model': RouteTarget}),
|
||||||
|
|
||||||
# RIRs
|
# RIRs
|
||||||
path('rirs/', views.RIRListView.as_view(), name='rir_list'),
|
path('rirs/', views.RIRListView.as_view(), name='rir_list'),
|
||||||
path('rirs/add/', views.RIREditView.as_view(), name='rir_add'),
|
path('rirs/add/', views.RIREditView.as_view(), name='rir_add'),
|
||||||
|
@ -16,7 +16,7 @@ from virtualization.models import VirtualMachine, VMInterface
|
|||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
from .utils import add_available_ipaddresses, add_available_prefixes, add_available_vlans
|
from .utils import add_available_ipaddresses, add_available_prefixes, add_available_vlans
|
||||||
|
|
||||||
|
|
||||||
@ -74,6 +74,56 @@ class VRFBulkDeleteView(BulkDeleteView):
|
|||||||
table = tables.VRFTable
|
table = tables.VRFTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Route targets
|
||||||
|
#
|
||||||
|
|
||||||
|
class RouteTargetListView(ObjectListView):
|
||||||
|
queryset = RouteTarget.objects.prefetch_related('tenant')
|
||||||
|
filterset = filters.RouteTargetFilterSet
|
||||||
|
filterset_form = forms.RouteTargetFilterForm
|
||||||
|
table = tables.RouteTargetTable
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetView(ObjectView):
|
||||||
|
queryset = RouteTarget.objects.all()
|
||||||
|
|
||||||
|
def get(self, request, pk):
|
||||||
|
routetarget = get_object_or_404(self.queryset, pk=pk)
|
||||||
|
|
||||||
|
return render(request, 'ipam/routetarget.html', {
|
||||||
|
'routetarget': routetarget,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetEditView(ObjectEditView):
|
||||||
|
queryset = RouteTarget.objects.all()
|
||||||
|
model_form = forms.RouteTargetForm
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetDeleteView(ObjectDeleteView):
|
||||||
|
queryset = RouteTarget.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetBulkImportView(BulkImportView):
|
||||||
|
queryset = RouteTarget.objects.all()
|
||||||
|
model_form = forms.RouteTargetCSVForm
|
||||||
|
table = tables.RouteTargetTable
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetBulkEditView(BulkEditView):
|
||||||
|
queryset = RouteTarget.objects.prefetch_related('tenant')
|
||||||
|
filterset = filters.RouteTargetFilterSet
|
||||||
|
table = tables.RouteTargetTable
|
||||||
|
form = forms.RouteTargetBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class RouteTargetBulkDeleteView(BulkDeleteView):
|
||||||
|
queryset = RouteTarget.objects.prefetch_related('tenant')
|
||||||
|
filterset = filters.RouteTargetFilterSet
|
||||||
|
table = tables.RouteTargetTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# RIRs
|
# RIRs
|
||||||
#
|
#
|
||||||
|
@ -331,6 +331,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% url 'ipam:vrf_list' %}">VRFs</a>
|
<a href="{% url 'ipam:vrf_list' %}">VRFs</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li{% if not perms.ipam.view_routetarget %} class="disabled"{% endif %}>
|
||||||
|
{% if perms.ipam.add_routetarget %}
|
||||||
|
<div class="buttons pull-right">
|
||||||
|
<a href="{% url 'ipam:routetarget_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
|
||||||
|
<a href="{% url 'ipam:routetarget_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{% url 'ipam:routetarget_list' %}">Route Targets</a>
|
||||||
|
</li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="dropdown-header">VLANs</li>
|
<li class="dropdown-header">VLANs</li>
|
||||||
<li{% if not perms.ipam.view_vlan %} class="disabled"{% endif %}>
|
<li{% if not perms.ipam.view_vlan %} class="disabled"{% endif %}>
|
||||||
|
98
netbox/templates/ipam/routetarget.html
Normal file
98
netbox/templates/ipam/routetarget.html
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load buttons %}
|
||||||
|
{% load custom_links %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load plugins %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<div class="row noprint">
|
||||||
|
<div class="col-sm-8 col-md-9">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li><a href="{% url 'ipam:routetarget_list' %}">Route Targets</a></li>
|
||||||
|
<li>{{ routetarget }}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4 col-md-3">
|
||||||
|
<form action="{% url 'ipam:routetarget_list' %}" method="get">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" name="q" class="form-control" placeholder="Search roue targets" />
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<span class="fa fa-search" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pull-right noprint">
|
||||||
|
{% plugin_buttons routetarget %}
|
||||||
|
{% if perms.ipam.add_routetarget %}
|
||||||
|
{% clone_button routetarget %}
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.ipam.change_routetarget %}
|
||||||
|
{% edit_button routetarget %}
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.ipam.delete_routetarget %}
|
||||||
|
{% delete_button routetarget %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<h1>{% block title %}Route target {{ routetarget }}{% endblock %}</h1>
|
||||||
|
{% include 'inc/created_updated.html' with obj=routetarget %}
|
||||||
|
<div class="pull-right noprint">
|
||||||
|
{% custom_links routetarget %}
|
||||||
|
</div>
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li role="presentation"{% if not active_tab %} class="active"{% endif %}>
|
||||||
|
<a href="{{ routetarget.get_absolute_url }}">Route Target</a>
|
||||||
|
</li>
|
||||||
|
{% if perms.extras.view_objectchange %}
|
||||||
|
<li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
|
||||||
|
<a href="{% url 'ipam:routetarget_changelog' pk=routetarget.pk %}">Change Log</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Route Target</strong>
|
||||||
|
</div>
|
||||||
|
<table class="table table-hover panel-body attr-table">
|
||||||
|
<tr>
|
||||||
|
<td>Name</td>
|
||||||
|
<td>{{ routetarget.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tenant</td>
|
||||||
|
<td>
|
||||||
|
{% if routetarget.tenant %}
|
||||||
|
<a href="{{ routetarget.tenant.get_absolute_url }}">{{ routetarget.tenant }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Description</td>
|
||||||
|
<td>{{ vrf.description|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% include 'extras/inc/tags_panel.html' with tags=routetarget.tags.all url='ipam:routetarget_list' %}
|
||||||
|
{% plugin_left_page routetarget %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
{% include 'inc/custom_fields_panel.html' with obj=routetarget %}
|
||||||
|
{% plugin_right_page routetarget %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{% plugin_full_width_page routetarget %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user