mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-25 12:59:59 -06:00
* VLANTranslationPolicy and VLANTranslationRule models and all associated UI classes
* Change VLANTranslationPolicy to a PrimaryModel and make name unique
* Add serializer classes to InterfaceSerializer
* Remake migrations
* Add GraphQL typing
* Skip tagged models in test
* Missing migration
* Remove get_absolute_url methods
* Remove package-lock.json
* Rebuild migration and add constraints and field options
* Rebuild migrations
* Use DynamicModelChoiceField for policy field
* Make vlan_translation_policy fields on filtersets more consistent with existing __name convention
* Add vlan_translation_table to VMInterface detail page
* Add vlan_translation_policy to VMInterfaceSerializer
* Move vlan_translation_policy fields to model and filterset mixins
* Protect in-use policies against deletion
* Add vlan_translation_policy to fields in VMInterfaceSerializer
* Cleanup indentation
* Remove unnecessary ordering column
* Rebuild migrations
* Search methods and registration
* Ensure 'id' column is present by default
* Add graphql types/filters/schema for VLANTranslationRule
* Filterset tests
* View tests
* API and viewset tests (incomplete)
* Add tags to VLANTranslationRuleForm
* Complete viewset tests for VLANTranslationRule
* Make VLANTranslationRule.policy nullable (but still required)
* Revert "Make VLANTranslationRule.policy nullable (but still required)"
This reverts commit 4c1bb437ef.
* Revert nullability
* Explicitly prefetch policy in graphql
* Documentation of new and affected models
* Add note about select_related in graphql
* Rework policy/rule documentation
* Move vlan_translation_policy into 802.1Q Switching fieldset
* Remove redundant InterfaceVLANTranslationTable
* Conditionally include vlan_translation_table in interface.html and vminterface.html
* Add description field to VLANTranslationRule
* Define vlan_translation_table conditionally
* Add policy (name) filter to VLANTranslationRuleFilterSet
* Revert changes to adding-models.md (moved to another PR)
* Dynamic table for linked rules in vlantranslationpolicy.html
* Misc cleanup
---------
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
@@ -8,7 +8,7 @@ from dcim.api.serializers_.sites import SiteSerializer
|
||||
from dcim.choices import InterfaceModeChoices
|
||||
from extras.api.serializers_.configtemplates import ConfigTemplateSerializer
|
||||
from ipam.api.serializers_.ip import IPAddressSerializer
|
||||
from ipam.api.serializers_.vlans import VLANSerializer
|
||||
from ipam.api.serializers_.vlans import VLANSerializer, VLANTranslationPolicySerializer
|
||||
from ipam.api.serializers_.vrfs import VRFSerializer
|
||||
from ipam.models import VLAN
|
||||
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
|
||||
@@ -89,6 +89,7 @@ class VMInterfaceSerializer(NetBoxModelSerializer):
|
||||
required=False,
|
||||
many=True
|
||||
)
|
||||
vlan_translation_policy = VLANTranslationPolicySerializer(nested=True, required=False, allow_null=True)
|
||||
vrf = VRFSerializer(nested=True, required=False, allow_null=True)
|
||||
l2vpn_termination = L2VPNTerminationSerializer(nested=True, read_only=True, allow_null=True)
|
||||
count_ipaddresses = serializers.IntegerField(read_only=True)
|
||||
@@ -105,6 +106,7 @@ class VMInterfaceSerializer(NetBoxModelSerializer):
|
||||
'id', 'url', 'display_url', 'display', 'virtual_machine', 'name', 'enabled', 'parent', 'bridge', 'mtu',
|
||||
'mac_address', 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'vrf', 'l2vpn_termination',
|
||||
'tags', 'custom_fields', 'created', 'last_updated', 'count_ipaddresses', 'count_fhrp_groups',
|
||||
'vlan_translation_policy',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'virtual_machine', 'name', 'description')
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from dcim.forms.common import InterfaceCommonForm
|
||||
from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site, SiteGroup
|
||||
from extras.models import ConfigTemplate
|
||||
from ipam.models import IPAddress, VLAN, VLANGroup, VRF
|
||||
from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
|
||||
from netbox.forms import NetBoxModelForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from utilities.forms import ConfirmationForm
|
||||
@@ -343,20 +343,25 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
|
||||
required=False,
|
||||
label=_('VRF')
|
||||
)
|
||||
vlan_translation_policy = DynamicModelChoiceField(
|
||||
queryset=VLANTranslationPolicy.objects.all(),
|
||||
required=False,
|
||||
label=_('VLAN Translation Policy')
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
FieldSet('virtual_machine', 'name', 'description', 'tags', name=_('Interface')),
|
||||
FieldSet('vrf', 'mac_address', name=_('Addressing')),
|
||||
FieldSet('mtu', 'enabled', name=_('Operation')),
|
||||
FieldSet('parent', 'bridge', name=_('Related Interfaces')),
|
||||
FieldSet('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans', name=_('802.1Q Switching')),
|
||||
FieldSet('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vlan_translation_policy', name=_('802.1Q Switching')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = VMInterface
|
||||
fields = [
|
||||
'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mac_address', 'mtu', 'description', 'mode',
|
||||
'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags',
|
||||
'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags', 'vlan_translation_policy',
|
||||
]
|
||||
labels = {
|
||||
'mode': '802.1Q Mode',
|
||||
|
||||
@@ -100,6 +100,7 @@ class VMInterfaceType(IPAddressesMixin, ComponentType):
|
||||
bridge: Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')] | None
|
||||
untagged_vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
|
||||
vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
|
||||
vlan_translation_policy: Annotated["VLANTranslationPolicyType", strawberry.lazy('ipam.graphql.types')] | None
|
||||
|
||||
tagged_vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
|
||||
bridge_interfaces: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.0.9 on 2024-10-11 19:45
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0074_vlantranslationpolicy_vlantranslationrule'),
|
||||
('virtualization', '0041_charfield_null_choices'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vminterface',
|
||||
name='vlan_translation_policy',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlantranslationpolicy'),
|
||||
),
|
||||
]
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from dcim.models import Device, DeviceRole, Platform, Region, Site, SiteGroup
|
||||
from ipam.models import IPAddress, VRF
|
||||
from ipam.models import IPAddress, VLANTranslationPolicy, VRF
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device
|
||||
from virtualization.choices import *
|
||||
@@ -561,6 +561,13 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
)
|
||||
VirtualMachine.objects.bulk_create(vms)
|
||||
|
||||
vlan_translation_policies = (
|
||||
VLANTranslationPolicy(name='Policy 1'),
|
||||
VLANTranslationPolicy(name='Policy 2'),
|
||||
VLANTranslationPolicy(name='Policy 3'),
|
||||
)
|
||||
VLANTranslationPolicy.objects.bulk_create(vlan_translation_policies)
|
||||
|
||||
interfaces = (
|
||||
VMInterface(
|
||||
virtual_machine=vms[0],
|
||||
@@ -569,7 +576,8 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
mtu=100,
|
||||
mac_address='00-00-00-00-00-01',
|
||||
vrf=vrfs[0],
|
||||
description='foobar1'
|
||||
description='foobar1',
|
||||
vlan_translation_policy=vlan_translation_policies[0],
|
||||
),
|
||||
VMInterface(
|
||||
virtual_machine=vms[1],
|
||||
@@ -578,7 +586,8 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
mtu=200,
|
||||
mac_address='00-00-00-00-00-02',
|
||||
vrf=vrfs[1],
|
||||
description='foobar2'
|
||||
description='foobar2',
|
||||
vlan_translation_policy=vlan_translation_policies[0],
|
||||
),
|
||||
VMInterface(
|
||||
virtual_machine=vms[2],
|
||||
@@ -658,6 +667,13 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
params = {'description': ['foobar1', 'foobar2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_vlan_translation_policy(self):
|
||||
vlan_translation_policies = VLANTranslationPolicy.objects.all()[:2]
|
||||
params = {'vlan_translation_policy_id': [vlan_translation_policies[0].pk, vlan_translation_policies[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'vlan_translation_policy': [vlan_translation_policies[0].name, vlan_translation_policies[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
class VirtualDiskTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = VirtualDisk.objects.all()
|
||||
|
||||
@@ -16,7 +16,7 @@ from dcim.models import Device
|
||||
from dcim.tables import DeviceTable
|
||||
from extras.views import ObjectConfigContextView
|
||||
from ipam.models import IPAddress
|
||||
from ipam.tables import InterfaceVLANTable
|
||||
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
||||
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||
from netbox.views import generic
|
||||
from tenancy.views import ObjectContactsView
|
||||
@@ -516,6 +516,14 @@ class VMInterfaceView(generic.ObjectView):
|
||||
orderable=False
|
||||
)
|
||||
|
||||
# Get VLAN translation rules
|
||||
vlan_translation_table = None
|
||||
if instance.vlan_translation_policy:
|
||||
vlan_translation_table = VLANTranslationRuleTable(
|
||||
data=instance.vlan_translation_policy.rules.all(),
|
||||
orderable=False
|
||||
)
|
||||
|
||||
# Get assigned VLANs and annotate whether each is tagged or untagged
|
||||
vlans = []
|
||||
if instance.untagged_vlan is not None:
|
||||
@@ -533,6 +541,7 @@ class VMInterfaceView(generic.ObjectView):
|
||||
return {
|
||||
'child_interfaces_table': child_interfaces_tables,
|
||||
'vlan_table': vlan_table,
|
||||
'vlan_translation_table': vlan_translation_table,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user