Files
netbox-plugin-services/netbox_services/engine/mappers.py
T
sven b1c9c7a40b modified: netbox_services/engine/__init__.py
modified:   netbox_services/engine/allocators.py
modified:   netbox_services/engine/context.py
new file:   netbox_services/engine/mappers.py
modified:   netbox_services/engine/resolver.py
new file:   netbox_services/engine/templating.py
modified:   netbox_services/forms.py
modified:   netbox_services/models.py
modified:   netbox_services/tests/test_allocators.py
new file:   netbox_services/tests/test_mappers.py
modified:   netbox_services/tests/test_resolver.py
new file:   netbox_services/tests/test_templating.py
Simplify layout to template and service instances
2026-08-17 15:12:42 +02:00

184 lines
9.7 KiB
Python

"""Central mapper for parent→child containment links.
Given a child model class and a parent model class, ``map_link`` returns
the field name on the child that should be set to connect it to its parent
in the NetBox object hierarchy (e.g. ``Interface.device``, ``IPAddress.assigned_object``).
This keeps the resolver free of per-node ``link`` boilerplate — the tree
author never specifies how children attach; the mapper knows.
"""
_LINK_MAP = {
# ── DCIM: Device components → Device ─────────────────────
("dcim.consoleport", "dcim.device"): "device",
("dcim.consoleserverport", "dcim.device"): "device",
("dcim.powerport", "dcim.device"): "device",
("dcim.poweroutlet", "dcim.device"): "device",
("dcim.interface", "dcim.device"): "device",
("dcim.frontport", "dcim.device"): "device",
("dcim.rearport", "dcim.device"): "device",
("dcim.modulebay", "dcim.device"): "device",
("dcim.devicebay", "dcim.device"): "device",
("dcim.inventoryitem", "dcim.device"): "device",
("dcim.module", "dcim.device"): "device",
("dcim.virtualdevicecontext", "dcim.device"): "device",
# ── DCIM: Components → Module ────────────────────────────
("dcim.consoleport", "dcim.module"): "module",
("dcim.consoleserverport", "dcim.module"): "module",
("dcim.powerport", "dcim.module"): "module",
("dcim.poweroutlet", "dcim.module"): "module",
("dcim.interface", "dcim.module"): "module",
("dcim.frontport", "dcim.module"): "module",
("dcim.rearport", "dcim.module"): "module",
# ── DCIM: Hierarchical (self-referential) ───────────────
("dcim.interface", "dcim.interface"): "parent",
("dcim.modulebay", "dcim.modulebay"): "parent",
("dcim.inventoryitem", "dcim.inventoryitem"): "parent",
("dcim.devicerole", "dcim.devicerole"): "parent",
("dcim.platform", "dcim.platform"): "parent",
("dcim.region", "dcim.region"): "parent",
("dcim.sitegroup", "dcim.sitegroup"): "parent",
("dcim.location", "dcim.location"): "parent",
# ── DCIM: Site hierarchy ─────────────────────────────────
("dcim.site", "dcim.region"): "region",
("dcim.site", "dcim.sitegroup"): "group",
("dcim.location", "dcim.site"): "site",
# ── DCIM: Device placement ───────────────────────────────
("dcim.device", "dcim.site"): "site",
("dcim.device", "dcim.location"): "location",
("dcim.device", "dcim.rack"): "rack",
("dcim.device", "dcim.cluster"): "cluster",
("dcim.device", "dcim.virtualchassis"): "virtual_chassis",
# ── DCIM: Rack ───────────────────────────────────────────
("dcim.rack", "dcim.site"): "site",
("dcim.rack", "dcim.location"): "location",
("dcim.rack", "dcim.rackgroup"): "group",
("dcim.rack", "dcim.rackrole"): "role",
("dcim.rack", "dcim.racktype"): "rack_type",
("dcim.rackreservation", "dcim.rack"): "rack",
# ── DCIM: Power ──────────────────────────────────────────
("dcim.powerpanel", "dcim.site"): "site",
("dcim.powerpanel", "dcim.location"): "location",
("dcim.powerfeed", "dcim.powerpanel"): "power_panel",
("dcim.powerfeed", "dcim.rack"): "rack",
# ── DCIM: Component inter-relations ──────────────────────
("dcim.poweroutlet", "dcim.powerport"): "power_port",
("dcim.frontport", "dcim.rearport"): "rear_port",
# ── DCIM: Cables ─────────────────────────────────────────
("dcim.cable", "dcim.cablebundle"): "bundle",
("dcim.cabletermination", "dcim.cable"): "cable",
# ── DCIM: GFK assignments ────────────────────────────────
("dcim.macaddress", "dcim.interface"): "assigned_object",
("dcim.macaddress", "virtualization.vminterface"): "assigned_object",
# ── DCIM: InventoryItem ──────────────────────────────────
("dcim.inventoryitem", "dcim.inventoryitemrole"): "role",
("dcim.inventoryitem", "dcim.manufacturer"): "manufacturer",
# ── DCIM: Type hierarchy ─────────────────────────────────
("dcim.devicetype", "dcim.manufacturer"): "manufacturer",
("dcim.moduletype", "dcim.manufacturer"): "manufacturer",
("dcim.platform", "dcim.manufacturer"): "manufacturer",
# ── DCIM: Templates → DeviceType ─────────────────────────
("dcim.consoleporttemplate", "dcim.devicetype"): "device_type",
("dcim.consoleserverporttemplate", "dcim.devicetype"): "device_type",
("dcim.powerporttemplate", "dcim.devicetype"): "device_type",
("dcim.poweroutlettemplate", "dcim.devicetype"): "device_type",
("dcim.interfacetemplate", "dcim.devicetype"): "device_type",
("dcim.frontporttemplate", "dcim.devicetype"): "device_type",
("dcim.rearporttemplate", "dcim.devicetype"): "device_type",
("dcim.modulebaytemplate", "dcim.devicetype"): "device_type",
("dcim.devicebaytemplate", "dcim.devicetype"): "device_type",
("dcim.inventoryitemtemplate", "dcim.devicetype"): "device_type",
# ── DCIM: Templates → ModuleType ─────────────────────────
("dcim.consoleporttemplate", "dcim.moduletype"): "module_type",
("dcim.consoleserverporttemplate", "dcim.moduletype"): "module_type",
("dcim.powerporttemplate", "dcim.moduletype"): "module_type",
("dcim.poweroutlettemplate", "dcim.moduletype"): "module_type",
("dcim.interfacetemplate", "dcim.moduletype"): "module_type",
("dcim.frontporttemplate", "dcim.moduletype"): "module_type",
("dcim.rearporttemplate", "dcim.moduletype"): "module_type",
("dcim.modulebaytemplate", "dcim.moduletype"): "module_type",
# ── IPAM: IPAddress → Interface/VMInterface/FHRP (GFK) ───
("ipam.ipaddress", "dcim.interface"): "assigned_object",
("ipam.ipaddress", "virtualization.vminterface"): "assigned_object",
("ipam.ipaddress", "ipam.fhrpgroup"): "assigned_object",
# ── IPAM: VLAN ───────────────────────────────────────────
("ipam.vlan", "ipam.vlangroup"): "group",
("ipam.vlan", "dcim.site"): "site",
# ── IPAM: Prefix scope (GFK) ─────────────────────────────
("ipam.prefix", "dcim.site"): "scope",
("ipam.prefix", "dcim.location"): "scope",
("ipam.prefix", "dcim.region"): "scope",
("ipam.prefix", "dcim.sitegroup"): "scope",
# ── IPAM: VLANGroup scope (GFK) ──────────────────────────
("ipam.vlangroup", "dcim.site"): "scope",
("ipam.vlangroup", "dcim.location"): "scope",
("ipam.vlangroup", "dcim.region"): "scope",
("ipam.vlangroup", "dcim.sitegroup"): "scope",
# ── IPAM: FHRPGroupAssignment ────────────────────────────
("ipam.fhrpgroupassignment", "ipam.fhrpgroup"): "group",
("ipam.fhrpgroupassignment", "dcim.interface"): "interface",
("ipam.fhrpgroupassignment", "virtualization.vminterface"): "interface",
# ── IPAM: Other ──────────────────────────────────────────
("ipam.aggregate", "ipam.rir"): "rir",
("ipam.asn", "ipam.rir"): "rir",
("ipam.asnrange", "ipam.rir"): "rir",
("ipam.vlantranslationrule", "ipam.vlantranslationpolicy"): "policy",
# ── IPAM: Service (NetBox core ipam.Service) ─────────────
("ipam.service", "dcim.device"): "parent",
("ipam.service", "virtualization.virtualmachine"): "parent",
# ── Virtualization ───────────────────────────────────────
("virtualization.vminterface", "virtualization.virtualmachine"): "virtual_machine",
("virtualization.vminterface", "virtualization.vminterface"): "parent",
("virtualization.virtualdisk", "virtualization.virtualmachine"): "virtual_machine",
("virtualization.virtualmachine", "virtualization.cluster"): "cluster",
("virtualization.virtualmachine", "dcim.site"): "site",
("virtualization.virtualmachine", "dcim.device"): "device",
("virtualization.cluster", "virtualization.clustergroup"): "group",
("virtualization.cluster", "virtualization.clustertype"): "type",
("virtualization.cluster", "dcim.site"): "scope",
("virtualization.cluster", "dcim.location"): "scope",
("virtualization.cluster", "dcim.region"): "scope",
("virtualization.cluster", "dcim.sitegroup"): "scope",
}
def _type_key(model):
return f"{model._meta.app_label}.{model._meta.model_name}"
def map_link(child_model, parent_model):
"""Return the field name on *child_model* that links it to *parent_model*.
Returns ``None`` when no containment relationship is known.
"""
return _LINK_MAP.get((_type_key(child_model), _type_key(parent_model)))
def register_link(child_type, parent_type, field):
"""Register or override a containment link.
*child_type* and *parent_type* are ``"app_label.model_name"`` strings.
"""
_LINK_MAP[(child_type, parent_type)] = field