mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-17 06:29:53 +02:00
refactor(virtualization): Port to declarative layout
Add declarative layout panels for Cluster, Cluster Group, Cluster Type, Virtual Disk, and VM Interface, including addressing, VLAN assignment, and FHRP group handling. Expand the declarative layout primitives: - add GFK attribute rendering support - add panel for rendering context-provided tables - update templates to support new panels/attrs Closes #20923
This commit is contained in:
@@ -3,6 +3,16 @@ from django.utils.translation import gettext_lazy as _
|
||||
from netbox.ui import attrs, panels
|
||||
|
||||
|
||||
class ClusterPanel(panels.ObjectAttributesPanel):
|
||||
name = attrs.TextAttr('name')
|
||||
type = attrs.RelatedObjectAttr('type', linkify=True)
|
||||
status = attrs.ChoiceAttr('status')
|
||||
description = attrs.TextAttr('description')
|
||||
group = attrs.RelatedObjectAttr('group', linkify=True)
|
||||
tenant = attrs.RelatedObjectAttr('tenant', linkify=True, grouped_by='group')
|
||||
scope = attrs.GenericForeignKeyAttr('scope', linkify=True)
|
||||
|
||||
|
||||
class VirtualMachinePanel(panels.ObjectAttributesPanel):
|
||||
name = attrs.TextAttr('name')
|
||||
status = attrs.ChoiceAttr('status')
|
||||
@@ -32,3 +42,35 @@ class VirtualMachineClusterPanel(panels.ObjectAttributesPanel):
|
||||
cluster = attrs.RelatedObjectAttr('cluster', linkify=True)
|
||||
cluster_type = attrs.RelatedObjectAttr('cluster.type', linkify=True)
|
||||
device = attrs.RelatedObjectAttr('device', linkify=True)
|
||||
|
||||
|
||||
class VirtualDiskPanel(panels.ObjectAttributesPanel):
|
||||
virtual_machine = attrs.RelatedObjectAttr('virtual_machine', linkify=True, label=_('Virtual Machine'))
|
||||
name = attrs.TextAttr('name')
|
||||
size = attrs.TemplatedAttr('size', template_name='virtualization/virtualdisk/attrs/size.html')
|
||||
description = attrs.TextAttr('description')
|
||||
|
||||
|
||||
class VMInterfacePanel(panels.ObjectAttributesPanel):
|
||||
virtual_machine = attrs.RelatedObjectAttr('virtual_machine', linkify=True, label=_('Virtual Machine'))
|
||||
name = attrs.TextAttr('name')
|
||||
enabled = attrs.BooleanAttr('enabled')
|
||||
parent = attrs.RelatedObjectAttr('parent_interface', linkify=True)
|
||||
bridge = attrs.RelatedObjectAttr('bridge', linkify=True)
|
||||
description = attrs.TextAttr('description')
|
||||
mtu = attrs.TextAttr('mtu', label=_('MTU'))
|
||||
mode = attrs.ChoiceAttr('mode', label=_('802.1Q Mode'))
|
||||
qinq_svlan = attrs.RelatedObjectAttr('qinq_svlan', linkify=True, label=_('Q-in-Q SVLAN'))
|
||||
tunnel_termination = attrs.RelatedObjectAttr('tunnel_termination.tunnel', linkify=True, label=_('Tunnel'))
|
||||
|
||||
|
||||
class VMInterfaceAddressingPanel(panels.ObjectAttributesPanel):
|
||||
title = _('Addressing')
|
||||
|
||||
primary_mac_address = attrs.TextAttr(
|
||||
'primary_mac_address', label=_('MAC Address'), style='font-monospace', copy_button=True
|
||||
)
|
||||
vrf = attrs.RelatedObjectAttr('vrf', linkify=True, label=_('VRF'))
|
||||
vlan_translation_policy = attrs.RelatedObjectAttr(
|
||||
'vlan_translation_policy', linkify=True, label=_('VLAN Translation')
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel
|
||||
from extras.views import ObjectConfigContextView, ObjectRenderConfigView
|
||||
from ipam.models import IPAddress, VLANGroup
|
||||
from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
|
||||
from ipam.ui.panels import FHRPGroupAssignmentsPanel
|
||||
from netbox.object_actions import (
|
||||
AddObject,
|
||||
BulkDelete,
|
||||
@@ -25,7 +26,14 @@ from netbox.object_actions import (
|
||||
EditObject,
|
||||
)
|
||||
from netbox.ui import actions, layout
|
||||
from netbox.ui.panels import CommentsPanel, ObjectsTablePanel, TemplatePanel
|
||||
from netbox.ui.panels import (
|
||||
CommentsPanel,
|
||||
ContextTablePanel,
|
||||
ObjectsTablePanel,
|
||||
OrganizationalObjectPanel,
|
||||
RelatedObjectsPanel,
|
||||
TemplatePanel,
|
||||
)
|
||||
from netbox.views import generic
|
||||
from utilities.query import count_related
|
||||
from utilities.query_functions import CollateAsChar
|
||||
@@ -54,6 +62,17 @@ class ClusterTypeListView(generic.ObjectListView):
|
||||
@register_model_view(ClusterType)
|
||||
class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = ClusterType.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
OrganizationalObjectPanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -121,6 +140,17 @@ class ClusterGroupListView(generic.ObjectListView):
|
||||
@register_model_view(ClusterGroup)
|
||||
class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = ClusterGroup.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
OrganizationalObjectPanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
RelatedObjectsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -202,6 +232,18 @@ class ClusterListView(generic.ObjectListView):
|
||||
@register_model_view(Cluster)
|
||||
class ClusterView(GetRelatedModelsMixin, generic.ObjectView):
|
||||
queryset = Cluster.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.ClusterPanel(),
|
||||
CommentsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
TemplatePanel('virtualization/panels/cluster_resources.html'),
|
||||
RelatedObjectsPanel(),
|
||||
CustomFieldsPanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
return {
|
||||
@@ -507,6 +549,7 @@ class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
|
||||
# VM interfaces
|
||||
#
|
||||
|
||||
|
||||
@register_model_view(VMInterface, 'list', path='', detail=False)
|
||||
class VMInterfaceListView(generic.ObjectListView):
|
||||
queryset = VMInterface.objects.all()
|
||||
@@ -518,6 +561,44 @@ class VMInterfaceListView(generic.ObjectListView):
|
||||
@register_model_view(VMInterface)
|
||||
class VMInterfaceView(generic.ObjectView):
|
||||
queryset = VMInterface.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.VMInterfacePanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
CustomFieldsPanel(),
|
||||
panels.VMInterfaceAddressingPanel(),
|
||||
FHRPGroupAssignmentsPanel(),
|
||||
],
|
||||
bottom_panels=[
|
||||
ObjectsTablePanel(
|
||||
model='ipam.IPaddress',
|
||||
filters={'vminterface_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'ipam.IPaddress',
|
||||
url_params={
|
||||
'virtual_machine': lambda ctx: ctx['object'].virtual_machine.pk,
|
||||
'vminterface': lambda ctx: ctx['object'].pk,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
ObjectsTablePanel(
|
||||
model='dcim.MACAddress',
|
||||
filters={'vminterface_id': lambda ctx: ctx['object'].pk},
|
||||
actions=[
|
||||
actions.AddObject(
|
||||
'dcim.MACAddress', url_params={'vminterface': lambda ctx: ctx['object'].pk}
|
||||
),
|
||||
],
|
||||
),
|
||||
ContextTablePanel('vlan_table', title=_('Assigned VLANs')),
|
||||
ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')),
|
||||
ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')),
|
||||
],
|
||||
)
|
||||
|
||||
def get_extra_context(self, request, instance):
|
||||
|
||||
@@ -623,6 +704,15 @@ class VirtualDiskListView(generic.ObjectListView):
|
||||
@register_model_view(VirtualDisk)
|
||||
class VirtualDiskView(generic.ObjectView):
|
||||
queryset = VirtualDisk.objects.all()
|
||||
layout = layout.SimpleLayout(
|
||||
left_panels=[
|
||||
panels.VirtualDiskPanel(),
|
||||
TagsPanel(),
|
||||
],
|
||||
right_panels=[
|
||||
CustomFieldsPanel(),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@register_model_view(VirtualDisk, 'add', detail=False)
|
||||
|
||||
Reference in New Issue
Block a user