Skip to content

Fix: Pass device context to tag render for Jinja2 values#110

Open
berlikm wants to merge 6 commits into
OpensourceICTSolutions:developmentfrom
berlikm:fix/tag-render-device-context
Open

Fix: Pass device context to tag render for Jinja2 values#110
berlikm wants to merge 6 commits into
OpensourceICTSolutions:developmentfrom
berlikm:fix/tag-render-device-context

Conversation

@berlikm

@berlikm berlikm commented Jul 13, 2026

Copy link
Copy Markdown

Summary

Fixes tag rendering so that inherited Jinja2 tags (e.g. environment, vendor) are rendered against the actual Device/VM being synced, not against the Site/Role/SiteGroup where the tag assignment lives.

Background

nbxSync inherits tags from the inheritance chain (Device → Site → SiteGroup → Role → etc). Previously, HostSync called assigned_tag.render() with no context, so Jinja2 templates like {{ object.name }} or {{ object.device_type.manufacturer.name }} rendered against the Site or Role — not the device. This produced incorrect tag values.

Changes

  • HostSync._get_sync_target() — resolves the actual Device/VM from the sync context (all_objects['_instance']), falling back to self.obj.assigned_object for direct assignments.
  • get_tag_attributes(), get_defined_macros(), get_hostinventory() — now pass object=sync_target to Jinja2 rendering.
  • Tag deduplication by (tag, value) — prevents Zabbix 400 errors when the same tag is inherited from multiple chain sources (e.g. Role + parent Role).
  • template_content.py — refactored to use get_assigned_zabbixobjects() for the device buttons extension (includes ConfigGroup-expanded interfaces).

Dependencies

Stacks on PR #115 (Site/Region inheritance). The base PR adds the inheritance chain, assignment models, and get_assigned_zabbixobjects() resolver that this PR depends on.

Merge order: #115 → this PR.

Testing

All existing tests pass. Verified on dev NetBox — environment tag correctly detects production/dev from device name, vendor tag renders manufacturer name.

Part of #121.

berlikm added 3 commits July 14, 2026 14:15
Adds Site, SiteGroup, and Region as valid assignment targets for all
ZabbixServerAssignment, ZabbixHostgroupAssignment, ZabbixTemplateAssignment,
ZabbixTagAssignment, ZabbixMacroAssignment, and ZabbixHostInventory objects.

This allows assignments made at the Site/SiteGroup/Region level to be
inherited by all devices and VMs at that site or below, via the existing
inheritance_chain mechanism.

Changes:
- Constants: Site/SiteGroup/Region added to ASSIGNMENT_MODELS, ASSIGNMENT_TYPE_TO_FIELD, PATH_LABELS
- Forms: All 7 assignment forms get Site/SiteGroup/Region DynamicModelChoiceField + FieldSet
- Settings: inheritance_chain extended with Site/SiteGroup/Region resolution paths
- Jobs: SyncHostJob._prepare_assignment() creates detached copies for inherited assignments
- HostSync: _get_sync_target() resolves actual Device/VM for Jinja2 rendering
- HostSync: get_tag_attributes() deduplicates tags by (tag, value) to prevent
  Zabbix 7 rejecting duplicate pairs inherited from multiple sources
- HostInterfaceSync: get_create_params() falls back to the device's primary IP
  when the interface has no IP assigned (e.g. when inherited from SiteGroup)
- HostInterfaceSync: errors are caught per-interface so one failing interface
  does not prevent the remaining interfaces and templates from being synced
- Inheritance: ConfigGroup interface expansion removed; interfaces are now
  resolved naturally via the inheritance chain with primary IP fallback
- SyncBase: _is_inherited_copy guard prevents save()/update_sync_info() on inherited copies
- Views: ZabbixSiteTabView, ZabbixSiteGroupTabView, ZabbixRegionTabView added
- Tests: 9 new tests for inheritance resolution

Tested: all 1076 existing tests pass. Verified end-to-end on dev NetBox.
- Filter inherited/direct ZabbixServerAssignment and ZabbixHostInterface by zabbixserver
- Resolve ConfigGroup interface IP from primary_ip4/primary_ip6
- Add seen-set cycle guard to resolve_path
- Fixes silent dropping of Site/SiteGroup assignments
@berlikm
berlikm force-pushed the fix/tag-render-device-context branch from c4f9987 to 94a2b67 Compare July 14, 2026 14:34
berlikm added 2 commits July 14, 2026 14:54
Fixes inherited Site/SiteGroup/Region assignments reading custom fields from
the intermediate object instead of the actual Device/VM.
When a ZabbixTagAssignment is inherited (e.g. from a DeviceRole, Platform,
or Site), the Jinja2 template value was rendered against the assigned
object — the Role/Platform — rather than the actual Device or VM being
synced. This meant templates referencing $`object.name`$ resolved to
the Role name instead of the device name.

This passes `object=sync_target` to `assigned_tag.render()`, mirroring
the existing pattern in `get_defined_macros()` and
`get_hostinventory()` which already render against the device.

Additionally, tags are deduplicated by (tag, value) to prevent Zabbix 7
from rejecting duplicate pairs that arise when the same assignment is
inherited from multiple sources in the chain (e.g. a tag on both a
specific role and its parent role).

Updated `DummyAssignedTag.render()` in test_hostsync.py to accept
`**kwargs`. Documented the behavior in dynamic_values.md.

Tested: all 1076 existing tests pass.
@berlikm

berlikm commented Jul 14, 2026

Copy link
Copy Markdown
Author

This PR is part of the zero-touch provisioning feature set described in #121. Please see that issue for the full architecture and motivation.

berlikm added a commit to berlikm/nbxsync that referenced this pull request Jul 15, 2026
…stinterfaces

resolve_inherited_zabbix_assignments() returns OrderedDict for all
collections (tags, macros, templates, hostinterfaces, hostgroups).
Iterating an OrderedDict yields keys (integers), not values.
This caused tags to be skipped entirely and templates to never attach.

Add _get_all_objects() helper that handles both dict and list by checking
for .values() attribute. Also pass object=sync_target to
assigned_tag.render() so Jinja2 templates render against the actual
device/VM, not the Role/Site.

This fix belongs in PR OpensourceICTSolutions#115 because it changes resolve_inherited_zabbix_
assignments() to return OrderedDicts. PR OpensourceICTSolutions#113 (exclude_tag) and OpensourceICTSolutions#110
(device context) layer on top and inherit the fix.
berlikm added a commit to berlikm/nbxsync that referenced this pull request Jul 15, 2026
…ntext

# Conflicts:
#	nbxsync/tests/utils/test_hostsync.py
#	nbxsync/utils/sync/hostsync.py
berlikm added a commit to berlikm/nbxsync that referenced this pull request Jul 16, 2026
…ce context in tag render

- Add 'or []' to all dict.get() calls on all_objects to handle None
  (from PR OpensourceICTSolutions#115 fix #A: normalize all_objects collections to lists)
- Change assigned_tag.render() to render(object=sync_target)
  (from PR OpensourceICTSolutions#110: device context was reverted in exclusion commit)
@berlikm
berlikm force-pushed the fix/tag-render-device-context branch from dc2cc0b to 89c8533 Compare July 16, 2026 07:33
@berlikm

berlikm commented Jul 16, 2026

Copy link
Copy Markdown
Author

Conflict resolution note for rebase

When rebasing this PR after #115 lands, there will be a conflict in nbxsync/utils/sync/hostsync.py on the tag loop line:

Correct resolution: Take #115's version (or [] normalization). This PR's render(object=sync_target) on the line below merges cleanly — no conflict there.

Verified in the integration-test branch: 1135 tests pass, E2E confirmed.

…e missing groups

- Pass object=self._get_sync_target() to group.render() so Jinja2
  templates like {{ object.role.name }} resolve correctly against the
  actual device being synced, not the assignment's assigned_object.
- Switch from search={'name': ...} to filter={'name': ...} for exact
  matching.
- If the hostgroup doesn't exist in Zabbix yet, create it on-demand
  rather than silently skipping it. This ensures dynamic groups like
  Sites/KR-AYN/KR-AYN-KEU and Roles/Switch Core are created during
  the first host sync.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant