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
184 lines
9.7 KiB
Python
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
|