mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
VLANTranslationPolicy and VLANTranslationRule models and all associated UI classes
This commit is contained in:
parent
ccb2480e98
commit
aaa166a3b9
@ -1396,6 +1396,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
|
||||
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans',
|
||||
name=_('Wireless')
|
||||
),
|
||||
FieldSet('vlan_translation_policy', name=_('VLAN Translation'))
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@ -1404,7 +1405,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
|
||||
'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag',
|
||||
'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode',
|
||||
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans',
|
||||
'untagged_vlan', 'tagged_vlans', 'vrf', 'tags',
|
||||
'untagged_vlan', 'tagged_vlans', 'vrf', 'tags', 'vlan_translation_policy',
|
||||
]
|
||||
widgets = {
|
||||
'speed': NumberWithOptions(
|
||||
|
@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.0.9 on 2024-10-08 17:12
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0191_module_bay_rebuild'),
|
||||
('ipam', '0071_vlantranslationpolicy_vlantranslationrule'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='vlan_translation_policy',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ipam.vlantranslationpolicy'),
|
||||
),
|
||||
]
|
@ -735,6 +735,13 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
|
||||
object_id_field='assigned_object_id',
|
||||
related_query_name='interface',
|
||||
)
|
||||
vlan_translation_policy = models.ForeignKey(
|
||||
to='ipam.VLANTranslationPolicy',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_('VLAN Translation Policy'),
|
||||
)
|
||||
|
||||
clone_fields = (
|
||||
'device', 'module', 'parent', 'bridge', 'lag', 'type', 'mgmt_only', 'mtu', 'mode', 'speed', 'duplex', 'rf_role',
|
||||
|
@ -18,7 +18,7 @@ from jinja2.exceptions import TemplateError
|
||||
from circuits.models import Circuit, CircuitTermination
|
||||
from extras.views import ObjectConfigContextView
|
||||
from ipam.models import ASN, IPAddress, VLANGroup
|
||||
from ipam.tables import InterfaceVLANTable
|
||||
from ipam.tables import InterfaceVLANTable, InterfaceVLANTranslationTable
|
||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||
from netbox.views import generic
|
||||
from tenancy.views import ObjectContactsView
|
||||
@ -2579,12 +2579,18 @@ class InterfaceView(generic.ObjectView):
|
||||
data=vlans,
|
||||
orderable=False
|
||||
)
|
||||
vlan_translation_table = InterfaceVLANTranslationTable(
|
||||
interface=instance,
|
||||
data=instance.vlan_translation_policy.rules.all() if instance.vlan_translation_policy else [],
|
||||
orderable=False
|
||||
)
|
||||
|
||||
return {
|
||||
'vdc_table': vdc_table,
|
||||
'bridge_interfaces_table': bridge_interfaces_tables,
|
||||
'child_interfaces_table': child_interfaces_tables,
|
||||
'vlan_table': vlan_table,
|
||||
'vlan_translation_table': vlan_translation_table,
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ from rest_framework import serializers
|
||||
from dcim.api.serializers_.sites import SiteSerializer
|
||||
from ipam.choices import *
|
||||
from ipam.constants import VLANGROUP_SCOPE_TYPES
|
||||
from ipam.models import VLAN, VLANGroup
|
||||
from ipam.models import VLAN, VLANGroup, VLANTranslationPolicy, VLANTranslationRule
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, IntegerRangeSerializer, RelatedObjectCountField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from tenancy.api.serializers_.tenants import TenantSerializer
|
||||
@ -18,6 +18,8 @@ __all__ = (
|
||||
'CreateAvailableVLANSerializer',
|
||||
'VLANGroupSerializer',
|
||||
'VLANSerializer',
|
||||
'VLANTranslationPolicySerializer',
|
||||
'VLANTranslationRuleSerializer',
|
||||
)
|
||||
|
||||
|
||||
@ -110,3 +112,17 @@ class CreateAvailableVLANSerializer(NetBoxModelSerializer):
|
||||
def validate(self, data):
|
||||
# Bypass model validation since we don't have a VID yet
|
||||
return data
|
||||
|
||||
|
||||
class VLANTranslationPolicySerializer(NetBoxModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationPolicy
|
||||
fields = ['name', 'description']
|
||||
|
||||
|
||||
class VLANTranslationRuleSerializer(NetBoxModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationRule
|
||||
fields = ['policy', 'local_vid', 'remote_vid']
|
||||
|
@ -37,6 +37,8 @@ __all__ = (
|
||||
'ServiceTemplateFilterSet',
|
||||
'VLANFilterSet',
|
||||
'VLANGroupFilterSet',
|
||||
'VLANTranslationPolicyFilterSet',
|
||||
'VLANTranslationRuleFilterSet',
|
||||
'VRFFilterSet',
|
||||
)
|
||||
|
||||
@ -1089,6 +1091,20 @@ class VLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class VLANTranslationPolicyFilterSet(NetBoxModelFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationPolicy
|
||||
fields = ('id', 'name', 'description')
|
||||
|
||||
|
||||
class VLANTranslationRuleFilterSet(NetBoxModelFilterSet):
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationRule
|
||||
fields = ('id', 'policy', 'local_vid', 'remote_vid')
|
||||
|
||||
|
||||
class ServiceTemplateFilterSet(NetBoxModelFilterSet):
|
||||
port = NumericArrayFilter(
|
||||
field_name='ports',
|
||||
|
@ -33,6 +33,8 @@ __all__ = (
|
||||
'ServiceTemplateBulkEditForm',
|
||||
'VLANBulkEditForm',
|
||||
'VLANGroupBulkEditForm',
|
||||
'VLANTranslationPolicyBulkEditForm',
|
||||
'VLANTranslationRuleBulkEditForm',
|
||||
'VRFBulkEditForm',
|
||||
)
|
||||
|
||||
@ -574,6 +576,29 @@ class VLANBulkEditForm(NetBoxModelBulkEditForm):
|
||||
)
|
||||
|
||||
|
||||
class VLANTranslationPolicyBulkEditForm(NetBoxModelBulkEditForm):
|
||||
description = forms.CharField(
|
||||
label=_('Description'),
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
model = VLANTranslationPolicy
|
||||
fieldsets = (
|
||||
FieldSet('description'),
|
||||
)
|
||||
nullable_fields = ('description',)
|
||||
|
||||
|
||||
class VLANTranslationRuleBulkEditForm(NetBoxModelBulkEditForm):
|
||||
|
||||
model = VLANTranslationRule
|
||||
fieldsets = (
|
||||
FieldSet('policy', 'local_vid', 'remote_vid'),
|
||||
)
|
||||
nullable_fields = ('description',)
|
||||
|
||||
|
||||
class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
||||
protocol = forms.ChoiceField(
|
||||
label=_('Protocol'),
|
||||
|
@ -29,6 +29,8 @@ __all__ = (
|
||||
'ServiceTemplateImportForm',
|
||||
'VLANImportForm',
|
||||
'VLANGroupImportForm',
|
||||
'VLANTranslationPolicyImportForm',
|
||||
'VLANTranslationRuleImportForm',
|
||||
'VRFImportForm',
|
||||
)
|
||||
|
||||
@ -464,6 +466,20 @@ class VLANImportForm(NetBoxModelImportForm):
|
||||
fields = ('site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description', 'comments', 'tags')
|
||||
|
||||
|
||||
class VLANTranslationPolicyImportForm(NetBoxModelImportForm):
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationPolicy
|
||||
fields = ('name', 'description', 'tags')
|
||||
|
||||
|
||||
class VLANTranslationRuleImportForm(NetBoxModelImportForm):
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationRule
|
||||
fields = ('policy', 'local_vid', 'remote_vid')
|
||||
|
||||
|
||||
class ServiceTemplateImportForm(NetBoxModelImportForm):
|
||||
protocol = CSVChoiceField(
|
||||
label=_('Protocol'),
|
||||
|
@ -28,6 +28,8 @@ __all__ = (
|
||||
'ServiceTemplateFilterForm',
|
||||
'VLANFilterForm',
|
||||
'VLANGroupFilterForm',
|
||||
'VLANTranslationPolicyFilterForm',
|
||||
'VLANTranslationRuleFilterForm',
|
||||
'VRFFilterForm',
|
||||
)
|
||||
|
||||
@ -460,6 +462,32 @@ class VLANGroupFilterForm(NetBoxModelFilterSetForm):
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
class VLANTranslationPolicyFilterForm(NetBoxModelFilterSetForm):
|
||||
model = VLANTranslationPolicy
|
||||
fieldsets = (
|
||||
FieldSet('q', 'filter_id', 'tag'),
|
||||
FieldSet('name', 'description', name=_('Attributes')),
|
||||
)
|
||||
name = forms.CharField(
|
||||
required=False,
|
||||
label=_('Name')
|
||||
)
|
||||
description = forms.CharField(
|
||||
required=False,
|
||||
label=_('Name')
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
class VLANTranslationRuleFilterForm(NetBoxModelFilterSetForm):
|
||||
model = VLANTranslationRule
|
||||
fieldsets = (
|
||||
FieldSet('q', 'filter_id', 'tag'),
|
||||
FieldSet('policy', 'local_vid', 'remote_vid', name=_('Attributes')),
|
||||
)
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
class VLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
model = VLAN
|
||||
fieldsets = (
|
||||
|
@ -41,6 +41,8 @@ __all__ = (
|
||||
'ServiceTemplateForm',
|
||||
'VLANForm',
|
||||
'VLANGroupForm',
|
||||
'VLANTranslationPolicyForm',
|
||||
'VLANTranslationRuleForm',
|
||||
'VRFForm',
|
||||
)
|
||||
|
||||
@ -654,6 +656,32 @@ class VLANForm(TenancyForm, NetBoxModelForm):
|
||||
]
|
||||
|
||||
|
||||
class VLANTranslationPolicyForm(NetBoxModelForm):
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('name', 'description', 'tags', name=_('VLAN Translation Policy')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationPolicy
|
||||
fields = [
|
||||
'name', 'description',
|
||||
]
|
||||
|
||||
|
||||
class VLANTranslationRuleForm(NetBoxModelForm):
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('policy', 'local_vid', 'remote_vid', name=_('VLAN Translation Rule')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = VLANTranslationRule
|
||||
fields = [
|
||||
'policy', 'local_vid', 'remote_vid',
|
||||
]
|
||||
|
||||
|
||||
class ServiceTemplateForm(NetBoxModelForm):
|
||||
ports = NumericArrayField(
|
||||
label=_('Ports'),
|
||||
|
@ -0,0 +1,51 @@
|
||||
# Generated by Django 5.0.9 on 2024-10-08 17:12
|
||||
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
import utilities.json
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0121_customfield_related_object_filter'),
|
||||
('ipam', '0070_vlangroup_vlan_id_ranges'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='VLANTranslationPolicy',
|
||||
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=utilities.json.CustomFieldJSONEncoder)),
|
||||
('slug', models.SlugField(max_length=100, unique=True)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('description', models.CharField(blank=True, max_length=200)),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'VLAN translation policy',
|
||||
'verbose_name_plural': 'VLAN translation policies',
|
||||
'ordering': ('name',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VLANTranslationRule',
|
||||
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=utilities.json.CustomFieldJSONEncoder)),
|
||||
('local_vid', models.IntegerField()),
|
||||
('remote_vid', models.IntegerField()),
|
||||
('policy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='ipam.vlantranslationpolicy')),
|
||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('policy', 'local_vid', 'remote_vid'),
|
||||
},
|
||||
),
|
||||
]
|
@ -11,13 +11,15 @@ from dcim.models import Interface
|
||||
from ipam.choices import *
|
||||
from ipam.constants import *
|
||||
from ipam.querysets import VLANQuerySet, VLANGroupQuerySet
|
||||
from netbox.models import OrganizationalModel, PrimaryModel
|
||||
from netbox.models import OrganizationalModel, PrimaryModel, NetBoxModel
|
||||
from utilities.data import check_ranges_overlap, ranges_to_string
|
||||
from virtualization.models import VMInterface
|
||||
|
||||
__all__ = (
|
||||
'VLAN',
|
||||
'VLANGroup',
|
||||
'VLANTranslationPolicy',
|
||||
'VLANTranslationRule',
|
||||
)
|
||||
|
||||
|
||||
@ -280,3 +282,57 @@ class VLAN(PrimaryModel):
|
||||
@property
|
||||
def l2vpn_termination(self):
|
||||
return self.l2vpn_terminations.first()
|
||||
|
||||
|
||||
class VLANTranslationPolicy(OrganizationalModel):
|
||||
name = models.CharField(
|
||||
verbose_name=_('name'),
|
||||
max_length=100,
|
||||
)
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
max_length=200,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('VLAN translation policy')
|
||||
verbose_name_plural = _('VLAN translation policies')
|
||||
ordering = ('name',)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('ipam:vlantranslationpolicy', args=[self.pk])
|
||||
|
||||
|
||||
class VLANTranslationRule(NetBoxModel):
|
||||
policy = models.ForeignKey(
|
||||
to=VLANTranslationPolicy,
|
||||
related_name='rules',
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
local_vid = models.IntegerField()
|
||||
remote_vid = models.IntegerField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('VLAN translation rule')
|
||||
ordering = ('policy', 'local_vid', 'remote_vid',)
|
||||
# Unique constraints are TBD
|
||||
# constraints = (
|
||||
# models.UniqueConstraint(
|
||||
# fields=('policy', 'local_vid'),
|
||||
# name='%(app_label)s_%(class)s_unique_policy_local_vid'
|
||||
# ),
|
||||
# models.UniqueConstraint(
|
||||
# fields=('policy', 'remote_vid'),
|
||||
# name='%(app_label)s_%(class)s_unique_policy_remote_vid'
|
||||
# ),
|
||||
# )
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.local_vid} -> {self.remote_vid} ({self.policy})'
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('ipam:vlantranslationrule', args=[self.pk])
|
||||
|
@ -16,6 +16,9 @@ __all__ = (
|
||||
'VLANMembersTable',
|
||||
'VLANTable',
|
||||
'VLANVirtualMachinesTable',
|
||||
'VLANTranslationPolicyTable',
|
||||
'VLANTranslationRuleTable',
|
||||
'InterfaceVLANTranslationTable',
|
||||
)
|
||||
|
||||
AVAILABLE_LABEL = mark_safe('<span class="badge text-bg-success">Available</span>')
|
||||
@ -244,3 +247,75 @@ class InterfaceVLANTable(NetBoxTable):
|
||||
def __init__(self, interface, *args, **kwargs):
|
||||
self.interface = interface
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
#
|
||||
# VLAN Translation
|
||||
#
|
||||
|
||||
class VLANTranslationPolicyTable(NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
)
|
||||
description = tables.Column(
|
||||
verbose_name=_('Description'),
|
||||
# linkify=True
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='ipam:vlantranslationpolicy_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = VLANTranslationPolicy
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'description', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'description')
|
||||
|
||||
|
||||
class VLANTranslationRuleTable(NetBoxTable):
|
||||
policy = tables.Column(
|
||||
verbose_name=_('Policy'),
|
||||
linkify=True
|
||||
)
|
||||
local_vid = tables.Column(
|
||||
verbose_name=_('Local VID'),
|
||||
linkify=True
|
||||
)
|
||||
remote_vid = tables.Column(
|
||||
verbose_name=_('Remote VID'),
|
||||
)
|
||||
tags = columns.TagColumn(
|
||||
url_name='ipam:vlantranslationrule_list'
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = VLANTranslationRule
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'policy', 'local_vid', 'remote_vid', 'tags', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'local_vid', 'remote_vid', 'policy')
|
||||
|
||||
|
||||
class InterfaceVLANTranslationTable(NetBoxTable):
|
||||
policy = tables.Column(
|
||||
verbose_name=_('Policy'),
|
||||
linkify=True
|
||||
)
|
||||
local_vid = tables.Column(
|
||||
verbose_name=_('Local VID'),
|
||||
linkify=True,
|
||||
)
|
||||
remote_vid = tables.Column(
|
||||
verbose_name=_('Remote VID'),
|
||||
)
|
||||
|
||||
class Meta(NetBoxTable.Meta):
|
||||
model = VLANTranslationRule
|
||||
fields = ('local_vid', 'remote_vid')
|
||||
default_columns = ('pk', 'local_vid', 'remote_vid', 'policy')
|
||||
|
||||
def __init__(self, interface, *args, **kwargs):
|
||||
self.interface = interface
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -116,6 +116,22 @@ urlpatterns = [
|
||||
path('vlans/delete/', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
|
||||
path('vlans/<int:pk>/', include(get_model_urls('ipam', 'vlan'))),
|
||||
|
||||
# VLAN Translation Policies
|
||||
path('vlan-translation-policies/', views.VLANTranslationPolicyListView.as_view(), name='vlantranslationpolicy_list'),
|
||||
path('vlan-translation-policies/add/', views.VLANTranslationPolicyEditView.as_view(), name='vlantranslationpolicy_add'),
|
||||
path('vlan-translation-policies/import/', views.VLANTranslationPolicyBulkImportView.as_view(), name='vlantranslationpolicy_import'),
|
||||
path('vlan-translation-policies/edit/', views.VLANTranslationPolicyBulkEditView.as_view(), name='vlantranslationpolicy_bulk_edit'),
|
||||
path('vlan-translation-policies/delete/', views.VLANTranslationPolicyBulkDeleteView.as_view(), name='vlantranslationpolicy_bulk_delete'),
|
||||
path('vlan-translation-policies/<int:pk>/', include(get_model_urls('ipam', 'vlantranslationpolicy'))),
|
||||
|
||||
# VLAN Translation Rules
|
||||
path('vlan-translation-rules/', views.VLANTranslationRuleListView.as_view(), name='vlantranslationrule_list'),
|
||||
path('vlan-translation-rules/add/', views.VLANTranslationRuleEditView.as_view(), name='vlantranslationrule_add'),
|
||||
path('vlan-translation-rules/import/', views.VLANTranslationRuleBulkImportView.as_view(), name='vlantranslationrule_import'),
|
||||
path('vlan-translation-rules/edit/', views.VLANTranslationRuleBulkEditView.as_view(), name='vlantranslationrule_bulk_edit'),
|
||||
path('vlan-translation-rules/delete/', views.VLANTranslationRuleBulkDeleteView.as_view(), name='vlantranslationrule_bulk_delete'),
|
||||
path('vlan-translation-rules/<int:pk>/', include(get_model_urls('ipam', 'vlantranslationrule'))),
|
||||
|
||||
# Service templates
|
||||
path('service-templates/', views.ServiceTemplateListView.as_view(), name='servicetemplate_list'),
|
||||
path('service-templates/add/', views.ServiceTemplateEditView.as_view(), name='servicetemplate_add'),
|
||||
|
@ -986,6 +986,106 @@ class VLANGroupVLANsView(generic.ObjectChildrenView):
|
||||
return queryset
|
||||
|
||||
|
||||
#
|
||||
# VLAN Translation Policies
|
||||
#
|
||||
|
||||
class VLANTranslationPolicyListView(generic.ObjectListView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
filterset = filtersets.VLANTranslationPolicyFilterSet
|
||||
filterset_form = forms.VLANTranslationPolicyFilterForm
|
||||
table = tables.VLANTranslationPolicyTable
|
||||
|
||||
|
||||
@register_model_view(VLANTranslationPolicy)
|
||||
class VLANTranslationPolicyView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
'related_models': self.get_related_models(request, instance),
|
||||
}
|
||||
|
||||
|
||||
@register_model_view(VLANTranslationPolicy, 'edit')
|
||||
class VLANTranslationPolicyEditView(generic.ObjectEditView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
form = forms.VLANTranslationPolicyForm
|
||||
|
||||
|
||||
@register_model_view(VLANTranslationPolicy, 'delete')
|
||||
class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
|
||||
|
||||
class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
model_form = forms.VLANTranslationPolicyImportForm
|
||||
|
||||
|
||||
class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
filterset = filtersets.VLANTranslationPolicyFilterSet
|
||||
table = tables.VLANTranslationPolicyTable
|
||||
form = forms.VLANTranslationPolicyBulkEditForm
|
||||
|
||||
|
||||
class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = VLANTranslationPolicy.objects.all()
|
||||
filterset = filtersets.VLANTranslationPolicyFilterSet
|
||||
table = tables.VLANTranslationPolicyTable
|
||||
|
||||
|
||||
#
|
||||
# VLAN Translation Policies
|
||||
#
|
||||
|
||||
class VLANTranslationRuleListView(generic.ObjectListView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
filterset = filtersets.VLANTranslationRuleFilterSet
|
||||
filterset_form = forms.VLANTranslationRuleFilterForm
|
||||
table = tables.VLANTranslationRuleTable
|
||||
|
||||
|
||||
@register_model_view(VLANTranslationRule)
|
||||
class VLANTranslationRuleView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
'related_models': self.get_related_models(request, instance),
|
||||
}
|
||||
|
||||
|
||||
@register_model_view(VLANTranslationRule, 'edit')
|
||||
class VLANTranslationRuleEditView(generic.ObjectEditView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
form = forms.VLANTranslationRuleForm
|
||||
|
||||
|
||||
@register_model_view(VLANTranslationRule, 'delete')
|
||||
class VLANTranslationRuleDeleteView(generic.ObjectDeleteView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
|
||||
|
||||
class VLANTranslationRuleBulkImportView(generic.BulkImportView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
model_form = forms.VLANTranslationRuleImportForm
|
||||
|
||||
|
||||
class VLANTranslationRuleBulkEditView(generic.BulkEditView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
filterset = filtersets.VLANTranslationRuleFilterSet
|
||||
table = tables.VLANTranslationRuleTable
|
||||
form = forms.VLANTranslationRuleBulkEditForm
|
||||
|
||||
|
||||
class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = VLANTranslationRule.objects.all()
|
||||
filterset = filtersets.VLANTranslationRuleFilterSet
|
||||
table = tables.VLANTranslationRuleTable
|
||||
|
||||
|
||||
#
|
||||
# FHRP groups
|
||||
#
|
||||
|
@ -194,6 +194,8 @@ IPAM_MENU = Menu(
|
||||
items=(
|
||||
get_model_item('ipam', 'vlan', _('VLANs')),
|
||||
get_model_item('ipam', 'vlangroup', _('VLAN Groups')),
|
||||
get_model_item('ipam', 'vlantranslationpolicy', _('VLAN Translation Policies')),
|
||||
get_model_item('ipam', 'vlantranslationrule', _('VLAN Translation Rules')),
|
||||
),
|
||||
),
|
||||
MenuGroup(
|
||||
|
@ -133,6 +133,10 @@
|
||||
<th scope="row">{% trans "VRF" %}</th>
|
||||
<td>{{ object.vrf|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "VLAN Translation" %}</th>
|
||||
<td>{{ object.vlan_translation_policy|linkify|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% if not object.is_virtual %}
|
||||
@ -355,6 +359,11 @@
|
||||
{% include 'inc/panel_table.html' with table=vlan_table heading="VLANs" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col col-md-12">
|
||||
{% include 'inc/panel_table.html' with table=vlan_translation_table heading="VLAN Translation" %}
|
||||
</div>
|
||||
</div>
|
||||
{% if object.is_bridge %}
|
||||
<div class="row mb-3">
|
||||
<div class="col col-md-12">
|
||||
|
37
netbox/templates/ipam/vlantranslationpolicy.html
Normal file
37
netbox/templates/ipam/vlantranslationpolicy.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-4">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "VLAN Translation Policy" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "DNS Name" %}</th>
|
||||
<td>{{ object.name|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Description" %}</th>
|
||||
<td>{{ object.description|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-8">
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
41
netbox/templates/ipam/vlantranslationrule.html
Normal file
41
netbox/templates/ipam/vlantranslationrule.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-4">
|
||||
<div class="card">
|
||||
<h2 class="card-header">{% trans "VLAN Translation Rule" %}</h2>
|
||||
<table class="table table-hover attr-table">
|
||||
<tr>
|
||||
<th scope="row">{% trans "Policy" %}</th>
|
||||
<td>{{ object.policy|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Local VID" %}</th>
|
||||
<td>{{ object.local_vid|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Remote VID" %}</th>
|
||||
<td>{{ object.remote_vid|placeholder }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-8">
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_right_page object %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.0.9 on 2024-10-08 17:12
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0071_vlantranslationpolicy_vlantranslationrule'),
|
||||
('virtualization', '0040_convert_disk_size'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vminterface',
|
||||
name='vlan_translation_policy',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ipam.vlantranslationpolicy'),
|
||||
),
|
||||
]
|
@ -372,6 +372,13 @@ class VMInterface(ComponentModel, BaseInterface, TrackingModelMixin):
|
||||
object_id_field='assigned_object_id',
|
||||
related_query_name='vminterface',
|
||||
)
|
||||
vlan_translation_policy = models.ForeignKey(
|
||||
to='ipam.VLANTranslationPolicy',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_('VLAN Translation Policy'),
|
||||
)
|
||||
|
||||
class Meta(ComponentModel.Meta):
|
||||
verbose_name = _('interface')
|
||||
|
Loading…
Reference in New Issue
Block a user