"""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