Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.py[cod]
25 changes: 25 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## [Unreleased]
### New features

- Added `exclude_tag` configuration setting to exclude hosts from Zabbix sync entirely via a ZabbixTag assigned to any object in the inheritance chain (`ZabbixTag.tag` name match)
- Added `ZabbixTemplateRule` for regex-based template (and optional hostgroup/tag) assignment by platform name (`re.search`, case-insensitive)
- Template rules support optional conjunctive criteria: `role_pattern`, `require_tags` (NetBox tag slugs) and `manufacturer` (fail-closed when set; `PROTECT` on delete). Optional hostgroup/tag FKs also use `PROTECT`
- Template rules that attach a hostgroup are shown on the Zabbix Hostgroup detail/list views
- Added a REST API endpoint for `ZabbixTemplateRule` (`/api/plugins/nbxsync/zabbixtemplaterule/`)
- Added Site/SiteGroup/Region inheritance paths (appended after role/platform so upgrades do not change Role/Platform precedence); cluster site uses `cluster._site` (available since NetBox 4.2; plugin requires ≥4.2.6)
- Added NetBox Tag as an assignment target: any assignment can be pointed at a Tag; tagged Devices/VMs/VDCs inherit it at object level with automatic add/remove lifecycle (`Tag: <name>` shown as source)

### Improvements

- Plugin requires NetBox ≥4.2.6 (`PluginConfig.min_version`)
- Inherited sync status on the Zabbix tab uses a neutral indicator (distinct from a direct local assignment)
- Default `backgroundsync.objects.interval` is 360 minutes so a full reconcile is less likely to overlap the next run

### Bug fixes

- VirtualMachines no longer inherit assignments via `device`-prefixed `inheritance_chain` paths (NetBox ≥4.3 `VirtualMachine.device`). Host manufacturer/role/device-type templates no longer leak onto guest VMs; Virtual Device Contexts still walk those paths
- Jinja2 tag and hostgroup values are rendered against the Device/VM being synchronised, not against the inheritance source (Role, Platform, Site, …)
- Tag/hostgroup Jinja context exposes `device`, `site`, `tenant`, `role`, `device_type`, and `manufacturer` aliases from the render object (covers #102; aliases follow the host during sync)
- UI previews for hierarchy assignments use a device-shaped view of the target object instead of borrowing a sample descendant device
- UI previews skip Devices/VMs carrying the configured `exclude_tag` when selecting a representative host

## [1.0.0] - Initial Release

- Loads of features, :)
Expand Down
72 changes: 69 additions & 3 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ The plugin is configuration to do exactly what you want, by means of the plugin
['cluster'],
['cluster', 'type'],
['type'],
# Hierarchy appended after device/role/platform (first-seen wins)
['device', 'site'],
['site'],
['site', 'group'],
['site', 'region'],
['cluster', '_site'],
],
'backgroundsync': {
'objects': {
'enabled': True,
'interval': 60, # 1 hour
'interval': 360, # 6 hours
},
'templates': {
'enabled': True,
Expand Down Expand Up @@ -105,10 +111,60 @@ The plugin is configuration to do exactly what you want, by means of the plugin
'objtag_type': 'nb_type',
'objtag_id': 'nb_id',
'custom_field_hostname':'',
'custom_field_display_name':''
'custom_field_display_name':'',
'exclude_tag': '',
}
```

## Inheritance Chain

The `inheritance_chain` setting defines which NetBox objects are traversed when resolving Zabbix assignments. Assignments (templates, tags, hostgroups, macros, proxy/server, interfaces, inventory, configuration groups) made on any object in the chain are inherited by the device or VM being synced, with direct assignments taking priority. Within inherited sources, **first path wins** (leaf-first order as listed).

### VirtualMachine and `device`-prefixed paths

Paths that start with `device` (for example `['device']`, `['device', 'role']`, `['device', 'device_type', 'manufacturer']`) describe the associated physical device.

Virtual Device Contexts keep these paths (a VDC is part of its parent device). VirtualMachines skip them: since NetBox 4.3, `VirtualMachine.device` links a guest to its hosting device, and walking that path would leak host hardware assignments onto the guest. VMs still inherit via cluster, site, role, platform and tag paths that apply to the VM itself.

### Site, SiteGroup, and Region Inheritance

Hierarchy paths are appended after device/role/platform/manufacturer/cluster paths so upgrading into Site inheritance does not silently override existing Role or Platform assignments. SiteGroup and Region ancestors are walked automatically when a group or region is reached.

| Path | Description |
|------|-------------|
| `['device', 'site']` | The device's site (also VDC → device → site; not walked for VirtualMachines) |
| `['site']` | Site (direct) |
| `['site', 'group']` | The site's SiteGroup (parents walked) |
| `['site', 'region']` | The site's region (parents walked) |
| `['cluster', '_site']` | The cluster's scoped site for VMs (`CachedScopeMixin._site`, NetBox 4.2+; plugin requires ≥4.2.6) |

If you previously customized `inheritance_chain` with Site paths ahead of Role/Platform, review hosts that have both a Site-level and a Role/Platform-level assignment — effective winners may change. Prefer appending hierarchy paths.

For example, assigning a `ZabbixServerAssignment` (proxy) to a `SiteGroup` means every device at every site in that SiteGroup inherits the proxy — no per-device assignment needed.

## Zabbix Template Rules

`ZabbixTemplateRule` assigns a Zabbix template (and optionally a hostgroup and tag) when a Device or VM matches the rule. The platform name is matched with case-insensitive `re.search`. Rules run after direct and inherited assignments, so explicit `ZabbixTemplateAssignment` objects always take priority.

Optional hostgroup/tag assignment is useful for OS-family grouping (for example a Windows rule that assigns the agent template, a `Windows` hostgroup and an `os_family=Windows` tag). Hostgroups attached by a rule appear on the Zabbix Hostgroup page under Template rules.

| Field | Description |
|-------|-------------|
| `name` | Human-readable name |
| `pattern` | Regex matched against platform name (`re.search`, case-insensitive). Use `.*` when matching only on role, tags or manufacturer |
| `role_pattern` | Optional regex against the Device/VM role name. Empty = any role |
| `require_tags` | Optional comma-separated NetBox tag slugs (all required). Empty = any. Uses object tags, not DeviceType tags |
| `manufacturer` | Optional Manufacturer. When set, `device_type.manufacturer` must match. Empty = any. Objects without a manufacturer (e.g. VMs) do not match. Uses `PROTECT` on delete |
| `zabbixtemplate` | Template assigned when the rule matches |
| `zabbixhostgroup` | Optional hostgroup assigned on match |
| `zabbixtag` | Optional tag assigned on match |
| `enabled` | Enable/disable without deleting the rule |
| `priority` | Lower value = higher priority |

All non-empty criteria are combined with AND. Patterns are validated on save; common nested-quantifier shapes such as `(a+)+` / `(a*){2,}` are rejected as a ReDoS guard (not a complete regex safety analyser). Platform names are capped at 64 characters (roles at 100). Optional hostgroups must belong to the same Zabbix server as the template.

Example: `pattern=.*`, `role_pattern=^Server$`, `manufacturer=Dell`, template = Dell iDRAC by SNMP — without assigning that template on every Dell Manufacturer object.

## Configuration values

### Source of Truth
Expand Down Expand Up @@ -157,7 +213,7 @@ Either true or false (default: True)

##### interval

Used to determine the interval to sync Devices and Virtual Machines to/from Zabbix, in minutes (default: 60)
Used to determine the interval to sync Devices and Virtual Machines to/from Zabbix, in minutes (default: 360 / 6 hours)

#### templates

Expand Down Expand Up @@ -270,6 +326,16 @@ These tags allow you to navigate from a Zabbix host back to the corresponding N
### custom_field_hostname and custom_field_display_name
You can use these fields to map the connection between NetBox and the Zabbix hostname and display name. The device name is used as the default.

### exclude_tag

When set to a non-empty string (e.g. `'do_not_monitor'`), any `ZabbixTagAssignment` with a tag matching this name — whether assigned directly on a Device/VM or inherited from a Role, Platform, Site, SiteGroup, Region, Manufacturer, or Configuration Group — causes the host to be excluded from Zabbix sync entirely. No Zabbix host is created, and if a host was previously synced it is deleted from Zabbix.

This is useful for excluding device classes that should never be monitored (e.g. desktop PCs, VDI sessions, test lab devices) without removing their Site or Platform assignments.

The tag itself is never pushed to Zabbix — it is only used as a signal during sync resolution and is filtered out before Jinja2 rendering.

Defaults to `''` (empty string = feature disabled).

## Enabling and Disabling Synchronization

Two separate `sync_enabled` flags control whether synchronization to Zabbix is active.
Expand Down
54 changes: 34 additions & 20 deletions docs/dynamic_values.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,11 @@ For example, if a Hostgroup is given the value:
{{ object.site.name }}
```

and is applied to a `Device`, it will render as the device's `Site Name`. Here, object refers to the entity to which the assignment is made. This distinction is important: if a `Hostgroup` is assigned to a `DeviceType`, then the `DeviceType` becomes the object—even if the `Hostgroup` is later inherited by a `Device`.
and is applied to a `Device`, it will render as the device's Site name.

Therefore, using the following template:
During host synchronisation, tag, hostgroup, macro and host-inventory templates are rendered against the Device, VDC or VirtualMachine being synced — even when the assignment is inherited from a Role, Platform, Site or similar. That way templates such as `{{ object.name }}` or `{{ object.site.name }}` resolve to the host, not to the inheritance source.

```jinja2
{{ object.site.name }}
```

on a `DeviceType` does not make sense, because a `DeviceType` does not have a `Site` attribute.
In the UI preview (and when syncing a hostgroup assignment against a hierarchy object itself), `object` is the assignment target. Hierarchy targets such as DeviceRole or Site are exposed in a device-shaped form (`object.role`, `object.site`, …) so templates like `Roles/{{ object.role.name }}` work without borrowing a descendant device. Targets that cannot fill a single device-shaped value leave the template unresolved.

## Context

Expand All @@ -30,40 +26,58 @@ Rendering a value is always performed within a context, which provides access to
{{ object.site.name }} (via {{ tag }})
```

would be perfectly valid.
would be perfectly valid, as would the shorthand aliases (same source as `object`):

```jinja2
{{ site.name }} / {{ device.name }}
{{ device_type.model }} — {{ manufacturer.name }}
```

Those aliases are filled from the render `object` after any `object=` override, so during host sync they follow the Device/VM, not the Role/Site assignment row. They are omitted when the attribute does not exist (`device` is only set for Device/VM/VDC).

### Tag

Tags are rendered within a context that includes the following information:

| Key | Value | Explanation |
|-------------|-----------------------|----------------------------------------------------------------------------------------------|
| object | assigned_object | Refers to the assigned object; this could be a DeviceType, Device, VirtualMachine, etc. |
| tag | zabbixtag.tag | Contains the Zabbix Tag value that this assignment refers to |
| value | zabbixtag.value | The value of the Zabbix Tag (typically the Jinja2 template) |
| name | zabbixtag.name | The name of the Zabbix Tag |
| description | zabbixtag.description | The description of the Zabbix Tag |
| object | assigned_object | The assignment target. During host sync this is the Device/VM/VDC being synced |
| device | render object | Set only when `object` is a Device, VM or VDC |
| site | `object.site` | Present when the render object has a site |
| tenant | `object.tenant` | Present when the render object has a tenant |
| role | `object.role` | Present when the render object has a role |
| device_type | `object.device_type` | Present when the render object has a device type |
| manufacturer | `device_type.manufacturer` | Present when `device_type` has a manufacturer |
| tag | zabbixtag.tag | Contains the Zabbix Tag value that this assignment refers to |
| value | zabbixtag.value | The value of the Zabbix Tag (typically the Jinja2 template) |
| name | zabbixtag.name | The name of the Zabbix Tag |
| description | zabbixtag.description | The description of the Zabbix Tag |

### Hostgroup

Just like tags, hostgroups are rendered in a context:

| Key | Value | Explanation |
|-------------|-----------------------|----------------------------------------------------------------------------------------------|
| object | assigned_object | Refers to the assigned object; this could be a DeviceType, Device, VirtualMachine, etc. |
| value | zabbixhostgroup.value | The value of the Zabbix Hostgroup (typically the Jinja2 template) |
| name | zabbixhostgroup.name | The name of the Zabbix Hostgroup |
| object | assigned_object | The assignment target. During host sync this is the Device/VM/VDC being synced |
| device | render object | Set only when `object` is a Device, VM or VDC |
| site | `object.site` | Present when the render object has a site |
| tenant | `object.tenant` | Present when the render object has a tenant |
| role | `object.role` | Present when the render object has a role |
| device_type | `object.device_type` | Present when the render object has a device type |
| manufacturer | `device_type.manufacturer` | Present when `device_type` has a manufacturer |
| value | zabbixhostgroup.value | The value of the Zabbix Hostgroup (typically the Jinja2 template) |
| name | zabbixhostgroup.name | The name of the Zabbix Hostgroup |

### Host Inventory

Each field on a `ZabbixHostInventory` record is rendered individually. The context is simpler than for tags and hostgroups:

| Key | Value | Explanation |
|----------|------------------|--------------------------------------------------------------|
| `object` | assigned_object | The Device, VDC, or VirtualMachine this inventory belongs to |
| `object` | assigned_object | The assignment target. During host sync this is the Device/VM/VDC being synced |

Because `ZabbixHostInventory` is assigned directly to a Device, VDC, or VM (never to a DeviceType or other inheritance-chain object), `object` always
refers to the host itself. This means device attributes like `object.site.name`, `object.rack.name`, or `object.primary_ip` are always available and always resolve to the correct host.
Inventory can be assigned on hierarchy objects and inherited; during host sync `object` is always the host, so fields such as `object.site.name` or `object.primary_ip` resolve correctly.

Note that each field has a maximum character length enforced at render time, values that exceed the limit are silently truncated. The `inventory_mode` field controls how Zabbix treats the inventory:
- `Manual` (the default) means Zabbix only updates inventory via the API, which is how nbxSync writes it.
Expand All @@ -73,4 +87,4 @@ Note that each field has a maximum character length enforced at render time, val
The `inventory_mode` field determines how Zabbix handles the inventory data.
- Manual (default) means only nbxSync writes to inventory.
- Automatic causes Zabbix to overwrite inventory from item values (this conflicts with nbxSync's writes and is not recommended).
- Disabled turns off inventory entirely for the host.
- Disabled turns off inventory entirely for the host.
8 changes: 7 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Also, replace `netbox_secrets` with `nbxsync` obviously.
## Normal install
### Prerequisites

- NetBox >= 4.x
- NetBox >= 4.2.6
- Python >= 3.8
- Zabbix server >= 7.0

Expand Down Expand Up @@ -85,6 +85,11 @@ PLUGINS_CONFIG = {
['cluster'],
['cluster', 'type'],
['type'],
['device', 'site'],
['site'],
['site', 'group'],
['site', 'region'],
['cluster', '_site'],
],
'backgroundsync': {
'objects': {
Expand Down Expand Up @@ -112,6 +117,7 @@ PLUGINS_CONFIG = {
'objtag_id': 'nb_id',
'custom_field_hostname': '',
'custom_field_display_name': '',
'exclude_tag': '',
}
}
```
Expand Down
Loading