138 lines
5.0 KiB
Python
138 lines
5.0 KiB
Python
from copy import deepcopy
|
|
from functools import wraps
|
|
|
|
from ipam.models import VLAN
|
|
from utilities.forms.fields import DynamicModelMultipleChoiceField
|
|
from utilities.forms.rendering import FieldSet
|
|
|
|
from .models import CableVLANAssignment, WirelessLinkVLANAssignment
|
|
|
|
FORM_FIELD = "utilities_vlans"
|
|
PATCH_MARKER = "_netbox_utilities_connection_vlans_installed"
|
|
|
|
|
|
def include_connection_vlans_in_fieldsets(fieldsets):
|
|
"""Add the connection VLAN field to an isolated copy of a form layout."""
|
|
if not fieldsets:
|
|
return (FieldSet(FORM_FIELD, name="VLANs der Verbindung"),)
|
|
|
|
copied_fieldsets = deepcopy(fieldsets)
|
|
|
|
def insert_into_link_group(group):
|
|
items = list(getattr(group, "items", ()))
|
|
if "description" in items or "tags" in items:
|
|
index = items.index("tags") if "tags" in items else len(items)
|
|
if FORM_FIELD not in items:
|
|
items.insert(index, FORM_FIELD)
|
|
group.items = tuple(items)
|
|
return True
|
|
|
|
for item in items:
|
|
if any(insert_into_link_group(nested) for nested in getattr(item, "groups", ())):
|
|
return True
|
|
return False
|
|
|
|
if any(insert_into_link_group(fieldset) for fieldset in copied_fieldsets):
|
|
return tuple(copied_fieldsets)
|
|
return (*copied_fieldsets, FieldSet(FORM_FIELD, name="VLANs der Verbindung"))
|
|
|
|
|
|
def _build_connection_vlan_form(base_form, assignment_model, target_field):
|
|
if getattr(base_form, PATCH_MARKER, False):
|
|
return base_form
|
|
|
|
class ConnectionVLANForm(base_form):
|
|
utilities_vlans = DynamicModelMultipleChoiceField(
|
|
queryset=VLAN.objects.all(),
|
|
required=False,
|
|
selector=True,
|
|
label="VLANs der Verbindung",
|
|
help_text=(
|
|
"Dokumentiert ein oder mehrere VLANs auf dieser Verbindung. "
|
|
"Die Tagged-/Untagged-VLAN-Einstellungen der Interfaces bleiben unverändert."
|
|
),
|
|
)
|
|
|
|
@property
|
|
def fieldsets(self):
|
|
return include_connection_vlans_in_fieldsets(super().fieldsets)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
if not self.is_bound and getattr(self.instance, "pk", None):
|
|
assignment = (
|
|
assignment_model.objects.filter(**{target_field: self.instance}).prefetch_related("vlans").first()
|
|
)
|
|
if assignment is not None:
|
|
self.initial[FORM_FIELD] = assignment.vlans.all()
|
|
|
|
def save(self, commit=True):
|
|
connection = super().save(commit=commit)
|
|
if commit:
|
|
self._save_connection_vlans(connection)
|
|
else:
|
|
native_save_m2m = self.save_m2m
|
|
|
|
def save_m2m():
|
|
native_save_m2m()
|
|
self._save_connection_vlans(connection)
|
|
|
|
self.save_m2m = save_m2m
|
|
return connection
|
|
|
|
def _save_connection_vlans(self, connection):
|
|
selected_vlans = list(self.cleaned_data.get(FORM_FIELD) or ())
|
|
lookup = {target_field: connection}
|
|
if not selected_vlans:
|
|
assignment_model.objects.filter(**lookup).delete()
|
|
return
|
|
assignment, _created = assignment_model.objects.get_or_create(**lookup)
|
|
assignment.vlans.set(selected_vlans)
|
|
|
|
ConnectionVLANForm.__name__ = f"ConnectionVLAN{base_form.__name__}"
|
|
ConnectionVLANForm.__qualname__ = ConnectionVLANForm.__name__
|
|
ConnectionVLANForm.__module__ = __name__
|
|
setattr(ConnectionVLANForm, PATCH_MARKER, True)
|
|
return ConnectionVLANForm
|
|
|
|
|
|
def _install_cable_vlan_form():
|
|
import dcim.forms
|
|
from dcim.forms import connections
|
|
from dcim.views import CableEditView
|
|
|
|
if getattr(CableEditView, PATCH_MARKER, False):
|
|
return
|
|
|
|
native_get_cable_form = dcim.forms.get_cable_form
|
|
|
|
@wraps(native_get_cable_form)
|
|
def get_connection_vlan_cable_form(*args, **kwargs):
|
|
base_form = native_get_cable_form(*args, **kwargs)
|
|
return _build_connection_vlan_form(base_form, CableVLANAssignment, "cable")
|
|
|
|
setattr(get_connection_vlan_cable_form, PATCH_MARKER, True)
|
|
dcim.forms.get_cable_form = get_connection_vlan_cable_form
|
|
connections.get_cable_form = get_connection_vlan_cable_form
|
|
CableEditView.template_name = "netbox_utilities/cable_edit.html"
|
|
CableEditView.htmx_template_name = "netbox_utilities/cable_edit_form.html"
|
|
setattr(CableEditView, PATCH_MARKER, True)
|
|
|
|
|
|
def _install_wireless_link_vlan_form():
|
|
from wireless.views import WirelessLinkEditView
|
|
|
|
if getattr(WirelessLinkEditView, PATCH_MARKER, False):
|
|
return
|
|
WirelessLinkEditView.form = _build_connection_vlan_form(
|
|
WirelessLinkEditView.form,
|
|
WirelessLinkVLANAssignment,
|
|
"wireless_link",
|
|
)
|
|
setattr(WirelessLinkEditView, PATCH_MARKER, True)
|
|
|
|
|
|
def install_connection_vlan_support():
|
|
_install_cable_vlan_form()
|
|
_install_wireless_link_vlan_form()
|