mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-20 10:16:42 -06:00
More fixes as a result of code review
This commit is contained in:
parent
0004b834fb
commit
30350e3b40
@ -464,9 +464,7 @@ class L2VPNSerializer(NetBoxModelSerializer):
|
|||||||
model = L2VPN
|
model = L2VPN
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', 'export_targets',
|
'id', 'url', 'display', 'identifier', 'name', 'slug', 'type', 'import_targets', 'export_targets',
|
||||||
'description', 'tenant',
|
'description', 'tenant', 'tags', 'custom_fields', 'created', 'last_updated'
|
||||||
# Extra Fields
|
|
||||||
'tags', 'custom_fields', 'created', 'last_updated'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -482,8 +480,7 @@ class L2VPNTerminationSerializer(NetBoxModelSerializer):
|
|||||||
model = L2VPNTermination
|
model = L2VPNTermination
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'l2vpn', 'assigned_object_type', 'assigned_object_id',
|
'id', 'url', 'display', 'l2vpn', 'assigned_object_type', 'assigned_object_id',
|
||||||
'assigned_object',
|
'assigned_object', 'tags', 'custom_fields', 'created', 'last_updated'
|
||||||
'tags', 'custom_fields', 'created', 'last_updated'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@swagger_serializer_method(serializer_or_field=serializers.DictField)
|
@swagger_serializer_method(serializer_or_field=serializers.DictField)
|
||||||
|
@ -191,6 +191,14 @@ class L2VPNTypeChoices(ChoiceSet):
|
|||||||
(TYPE_VPWS, 'VPWS'),
|
(TYPE_VPWS, 'VPWS'),
|
||||||
(TYPE_VPLS, 'VPLS'),
|
(TYPE_VPLS, 'VPLS'),
|
||||||
)),
|
)),
|
||||||
|
('VXLAN', (
|
||||||
|
(TYPE_VXLAN, 'VXLAN'),
|
||||||
|
(TYPE_VXLAN_EVPN, 'VXLAN-EVPN'),
|
||||||
|
)),
|
||||||
|
('L2VPN E-VPN', (
|
||||||
|
(TYPE_MPLS_EVPN, 'MPLS EVPN'),
|
||||||
|
(TYPE_PBB_EVPN, 'PBB EVPN'),
|
||||||
|
)),
|
||||||
('E-Line', (
|
('E-Line', (
|
||||||
(TYPE_EPL, 'EPL'),
|
(TYPE_EPL, 'EPL'),
|
||||||
(TYPE_EVPL, 'EVPL'),
|
(TYPE_EVPL, 'EVPL'),
|
||||||
@ -203,14 +211,6 @@ class L2VPNTypeChoices(ChoiceSet):
|
|||||||
(TYPE_EPTREE, 'Ethernet Private Tree'),
|
(TYPE_EPTREE, 'Ethernet Private Tree'),
|
||||||
(TYPE_EVPTREE, 'Ethernet Virtual Private Tree'),
|
(TYPE_EVPTREE, 'Ethernet Virtual Private Tree'),
|
||||||
)),
|
)),
|
||||||
('VXLAN', (
|
|
||||||
(TYPE_VXLAN, 'VXLAN'),
|
|
||||||
(TYPE_VXLAN_EVPN, 'VXLAN-EVPN'),
|
|
||||||
)),
|
|
||||||
('L2VPN E-VPN', (
|
|
||||||
(TYPE_MPLS_EVPN, 'MPLS EVPN'),
|
|
||||||
(TYPE_PBB_EVPN, 'PBB EVPN'),
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
P2P = (
|
P2P = (
|
||||||
|
@ -929,6 +929,20 @@ class L2VPNTerminationForm(NetBoxModelForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
virtual_machine = DynamicModelChoiceField(
|
||||||
|
queryset=VirtualMachine.objects.all(),
|
||||||
|
required=False,
|
||||||
|
query_params={}
|
||||||
|
)
|
||||||
|
|
||||||
|
vminterface = DynamicModelChoiceField(
|
||||||
|
queryset=VMInterface.objects.all(),
|
||||||
|
required=False,
|
||||||
|
query_params={
|
||||||
|
'virtual_machine_id': '$virtual_machine'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = L2VPNTermination
|
model = L2VPNTermination
|
||||||
fields = ('l2vpn', )
|
fields = ('l2vpn', )
|
||||||
@ -943,6 +957,8 @@ class L2VPNTerminationForm(NetBoxModelForm):
|
|||||||
initial['interface'] = instance.assigned_object
|
initial['interface'] = instance.assigned_object
|
||||||
elif type(instance.assigned_object) is VLAN:
|
elif type(instance.assigned_object) is VLAN:
|
||||||
initial['vlan'] = instance.assigned_object
|
initial['vlan'] = instance.assigned_object
|
||||||
|
elif type(instance.assigned_object) is VMInterface:
|
||||||
|
initial['vminterface'] = instance.assigned_object
|
||||||
kwargs['initial'] = initial
|
kwargs['initial'] = initial
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -950,11 +966,21 @@ class L2VPNTerminationForm(NetBoxModelForm):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
if not (self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')):
|
interface = self.cleaned_data.get('interface')
|
||||||
|
vlan = self.cleaned_data.get('vlan')
|
||||||
|
vminterface = self.cleaned_data.get('vminterface')
|
||||||
|
|
||||||
|
if not (interface or vlan or vminterface):
|
||||||
raise ValidationError('You must have either a interface or a VLAN')
|
raise ValidationError('You must have either a interface or a VLAN')
|
||||||
|
|
||||||
if self.cleaned_data.get('interface') and self.cleaned_data.get('vlan'):
|
if interface and vlan and vminterface:
|
||||||
|
raise ValidationError('Cannot assign a interface, vlan and vminterface')
|
||||||
|
elif interface and vlan:
|
||||||
raise ValidationError('Cannot assign both a interface and vlan')
|
raise ValidationError('Cannot assign both a interface and vlan')
|
||||||
|
elif interface and vminterface:
|
||||||
|
raise ValidationError('Cannot assign both a interface and vminterface')
|
||||||
|
elif vlan and vminterface:
|
||||||
|
raise ValidationError('Cannot assign both a vlan and vminterface')
|
||||||
|
|
||||||
obj = self.cleaned_data.get('interface') or self.cleaned_data.get('vlan')
|
obj = interface or vlan or vminterface
|
||||||
self.instance.assigned_object = obj
|
self.instance.assigned_object = obj
|
||||||
|
@ -60,23 +60,15 @@ class L2VPNTermination(NetBoxModel):
|
|||||||
l2vpn = models.ForeignKey(
|
l2vpn = models.ForeignKey(
|
||||||
to='ipam.L2VPN',
|
to='ipam.L2VPN',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='terminations',
|
related_name='terminations'
|
||||||
blank=False,
|
|
||||||
null=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assigned_object_type = models.ForeignKey(
|
assigned_object_type = models.ForeignKey(
|
||||||
to=ContentType,
|
to=ContentType,
|
||||||
limit_choices_to=L2VPN_ASSIGNMENT_MODELS,
|
limit_choices_to=L2VPN_ASSIGNMENT_MODELS,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+',
|
related_name='+'
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
assigned_object_id = models.PositiveBigIntegerField(
|
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
)
|
||||||
|
assigned_object_id = models.PositiveBigIntegerField()
|
||||||
assigned_object = GenericForeignKey(
|
assigned_object = GenericForeignKey(
|
||||||
ct_field='assigned_object_type',
|
ct_field='assigned_object_type',
|
||||||
fk_field='assigned_object_id'
|
fk_field='assigned_object_id'
|
||||||
@ -95,13 +87,13 @@ class L2VPNTermination(NetBoxModel):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.pk is not None:
|
if self.pk is not None:
|
||||||
return f'{self.assigned_object} <> {self.l2vpn}'
|
return f'{self.assigned_object} <> {self.l2vpn}'
|
||||||
return ''
|
return super().__str__()
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('ipam:l2vpntermination', args=[self.pk])
|
return reverse('ipam:l2vpntermination', args=[self.pk])
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
# Only check is assigned_object is set
|
# Only check is assigned_object is set. Required otherwise we have an Integrity Error thrown.
|
||||||
if self.assigned_object:
|
if self.assigned_object:
|
||||||
obj_id = self.assigned_object.pk
|
obj_id = self.assigned_object.pk
|
||||||
obj_type = ContentType.objects.get_for_model(self.assigned_object)
|
obj_type = ContentType.objects.get_for_model(self.assigned_object)
|
||||||
|
@ -9,25 +9,44 @@ __all__ = (
|
|||||||
'L2VPNTerminationTable',
|
'L2VPNTerminationTable',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
L2VPN_TARGETS = """
|
||||||
|
{% for rt in value.all %}
|
||||||
|
<a href="{{ rt.get_absolute_url }}">{{ rt }}</a>{% if not forloop.last %}<br />{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class L2VPNTable(NetBoxTable):
|
class L2VPNTable(NetBoxTable):
|
||||||
pk = columns.ToggleColumn()
|
pk = columns.ToggleColumn()
|
||||||
name = tables.Column(
|
name = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
import_targets = columns.TemplateColumn(
|
||||||
|
template_code=L2VPN_TARGETS,
|
||||||
|
orderable=False
|
||||||
|
)
|
||||||
|
export_targets = columns.TemplateColumn(
|
||||||
|
template_code=L2VPN_TARGETS,
|
||||||
|
orderable=False
|
||||||
|
)
|
||||||
|
|
||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = L2VPN
|
model = L2VPN
|
||||||
fields = ('pk', 'name', 'description', 'slug', 'type', 'tenant', 'actions')
|
fields = ('pk', 'name', 'slug', 'type', 'description', 'import_targets', 'export_targets', 'tenant', 'actions')
|
||||||
default_columns = ('pk', 'name', 'description', 'actions')
|
default_columns = ('pk', 'name', 'type', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
class L2VPNTerminationTable(NetBoxTable):
|
class L2VPNTerminationTable(NetBoxTable):
|
||||||
pk = columns.ToggleColumn()
|
pk = columns.ToggleColumn()
|
||||||
|
l2vpn = tables.Column(
|
||||||
|
verbose_name='L2VPN',
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
assigned_object_type = columns.ContentTypeColumn(
|
assigned_object_type = columns.ContentTypeColumn(
|
||||||
verbose_name='Object Type'
|
verbose_name='Object Type'
|
||||||
)
|
)
|
||||||
assigned_object = tables.Column(
|
assigned_object = tables.Column(
|
||||||
|
verbose_name='Assigned Object',
|
||||||
linkify=True,
|
linkify=True,
|
||||||
orderable=False
|
orderable=False
|
||||||
)
|
)
|
||||||
|
@ -187,25 +187,25 @@ urlpatterns = [
|
|||||||
path('services/<int:pk>/journal/', ObjectJournalView.as_view(), name='service_journal', kwargs={'model': Service}),
|
path('services/<int:pk>/journal/', ObjectJournalView.as_view(), name='service_journal', kwargs={'model': Service}),
|
||||||
|
|
||||||
# L2VPN
|
# L2VPN
|
||||||
path('l2vpn/', views.L2VPNListView.as_view(), name='l2vpn_list'),
|
path('l2vpns/', views.L2VPNListView.as_view(), name='l2vpn_list'),
|
||||||
path('l2vpn/add/', views.L2VPNEditView.as_view(), name='l2vpn_add'),
|
path('l2vpns/add/', views.L2VPNEditView.as_view(), name='l2vpn_add'),
|
||||||
path('l2vpn/import/', views.L2VPNBulkImportView.as_view(), name='l2vpn_import'),
|
path('l2vpns/import/', views.L2VPNBulkImportView.as_view(), name='l2vpn_import'),
|
||||||
path('l2vpn/edit/', views.L2VPNBulkEditView.as_view(), name='l2vpn_bulk_edit'),
|
path('l2vpns/edit/', views.L2VPNBulkEditView.as_view(), name='l2vpn_bulk_edit'),
|
||||||
path('l2vpn/delete/', views.L2VPNBulkDeleteView.as_view(), name='l2vpn_bulk_delete'),
|
path('l2vpns/delete/', views.L2VPNBulkDeleteView.as_view(), name='l2vpn_bulk_delete'),
|
||||||
path('l2vpn/<int:pk>/', views.L2VPNView.as_view(), name='l2vpn'),
|
path('l2vpns/<int:pk>/', views.L2VPNView.as_view(), name='l2vpn'),
|
||||||
path('l2vpn/<int:pk>/edit/', views.L2VPNEditView.as_view(), name='l2vpn_edit'),
|
path('l2vpns/<int:pk>/edit/', views.L2VPNEditView.as_view(), name='l2vpn_edit'),
|
||||||
path('l2vpn/<int:pk>/delete/', views.L2VPNDeleteView.as_view(), name='l2vpn_delete'),
|
path('l2vpns/<int:pk>/delete/', views.L2VPNDeleteView.as_view(), name='l2vpn_delete'),
|
||||||
path('l2vpn/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='l2vpn_changelog', kwargs={'model': L2VPN}),
|
path('l2vpns/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='l2vpn_changelog', kwargs={'model': L2VPN}),
|
||||||
path('l2vpn/<int:pk>/journal/', ObjectJournalView.as_view(), name='l2vpn_journal', kwargs={'model': L2VPN}),
|
path('l2vpns/<int:pk>/journal/', ObjectJournalView.as_view(), name='l2vpn_journal', kwargs={'model': L2VPN}),
|
||||||
|
|
||||||
path('l2vpn-termination/', views.L2VPNTerminationListView.as_view(), name='l2vpntermination_list'),
|
path('l2vpn-terminations/', views.L2VPNTerminationListView.as_view(), name='l2vpntermination_list'),
|
||||||
path('l2vpn-termination/add/', views.L2VPNTerminationEditView.as_view(), name='l2vpntermination_add'),
|
path('l2vpn-terminations/add/', views.L2VPNTerminationEditView.as_view(), name='l2vpntermination_add'),
|
||||||
path('l2vpn-termination/import/', views.L2VPNTerminationBulkImportView.as_view(), name='l2vpntermination_import'),
|
path('l2vpn-terminations/import/', views.L2VPNTerminationBulkImportView.as_view(), name='l2vpntermination_import'),
|
||||||
path('l2vpn-termination/edit/', views.L2VPNTerminationBulkEditView.as_view(), name='l2vpntermination_bulk_edit'),
|
path('l2vpn-terminations/edit/', views.L2VPNTerminationBulkEditView.as_view(), name='l2vpntermination_bulk_edit'),
|
||||||
path('l2vpn-termination/delete/', views.L2VPNTerminationBulkDeleteView.as_view(), name='l2vpntermination_bulk_delete'),
|
path('l2vpn-terminations/delete/', views.L2VPNTerminationBulkDeleteView.as_view(), name='l2vpntermination_bulk_delete'),
|
||||||
path('l2vpn-termination/<int:pk>/', views.L2VPNTerminationView.as_view(), name='l2vpntermination'),
|
path('l2vpn-terminations/<int:pk>/', views.L2VPNTerminationView.as_view(), name='l2vpntermination'),
|
||||||
path('l2vpn-termination/<int:pk>/edit/', views.L2VPNTerminationEditView.as_view(), name='l2vpntermination_edit'),
|
path('l2vpn-terminations/<int:pk>/edit/', views.L2VPNTerminationEditView.as_view(), name='l2vpntermination_edit'),
|
||||||
path('l2vpn-termination/<int:pk>/delete/', views.L2VPNTerminationDeleteView.as_view(), name='l2vpntermination_delete'),
|
path('l2vpn-terminations/<int:pk>/delete/', views.L2VPNTerminationDeleteView.as_view(), name='l2vpntermination_delete'),
|
||||||
path('l2vpn-termination/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='l2vpntermination_changelog', kwargs={'model': L2VPNTermination}),
|
path('l2vpn-terminations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='l2vpntermination_changelog', kwargs={'model': L2VPNTermination}),
|
||||||
path('l2vpn-termination/<int:pk>/journal/', ObjectJournalView.as_view(), name='l2vpntermination_journal', kwargs={'model': L2VPNTermination}),
|
path('l2vpn-terminations/<int:pk>/journal/', ObjectJournalView.as_view(), name='l2vpntermination_journal', kwargs={'model': L2VPNTermination}),
|
||||||
]
|
]
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<div class="offset-sm-3">
|
<div class="offset-sm-3">
|
||||||
<ul class="nav nav-pills" role="tablist">
|
<ul class="nav nav-pills" role="tablist">
|
||||||
<li role="presentation" class="nav-item">
|
<li role="presentation" class="nav-item">
|
||||||
<button role="tab" type="button" id="vlan_tab" data-bs-toggle="tab" aria-controls="vlan" data-bs-target="#vlan" class="nav-link {% if not form.initial.interface %}active{% endif %}">
|
<button role="tab" type="button" id="vlan_tab" data-bs-toggle="tab" aria-controls="vlan" data-bs-target="#vlan" class="nav-link {% if not form.initial.interface or form.initial.vminterface %}active{% endif %}">
|
||||||
VLAN
|
VLAN
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@ -21,18 +21,28 @@
|
|||||||
Interface
|
Interface
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li role="presentation" class="nav-item">
|
||||||
|
<button role="tab" type="button" id="vminterface_tab" data-bs-toggle="tab" aria-controls="vminterface" data-bs-target="#vminterface" class="nav-link {% if form.initial.vminterface %}active{% endif %}">
|
||||||
|
VM Interface
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="tab-content p-0 border-0">
|
<div class="tab-content p-0 border-0">
|
||||||
|
<div class="tab-pane {% if not form.initial.interface or form.initial.vminterface %}active{% endif %}" id="vlan" role="tabpanel" aria-labeled-by="vlan_tab">
|
||||||
{% render_field form.device %}
|
{% render_field form.device %}
|
||||||
<div class="tab-pane {% if not form.initial.interface %}active{% endif %}" id="vlan" role="tabpanel" aria-labeled-by="vlan_tab">
|
|
||||||
{% render_field form.vlan %}
|
{% render_field form.vlan %}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane {% if form.initial.interface %}active{% endif %}" id="interface" role="tabpanel" aria-labeled-by="interface_tab">
|
<div class="tab-pane {% if form.initial.interface %}active{% endif %}" id="interface" role="tabpanel" aria-labeled-by="interface_tab">
|
||||||
|
{% render_field form.device %}
|
||||||
{% render_field form.interface %}
|
{% render_field form.interface %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane {% if form.initial.vminterface %}active{% endif %}" id="vminterface" role="tabpanel" aria-labeled-by="vminterface_tab">
|
||||||
|
{% render_field form.virtual_machine %}
|
||||||
|
{% render_field form.vminterface %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user