Skip to content

Commit 7fbb4c7

Browse files
authored
Add template group creation (#66)
1 parent b4ee36c commit 7fbb4c7

File tree

6 files changed

+158
-25
lines changed

6 files changed

+158
-25
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# About
22

3-
Zabbix-auto-config is an utility that aims to automatically configure hosts, host groups, host inventories and templates in the monitoring software [Zabbix](https://www.zabbix.com/).
3+
Zabbix-auto-config is an utility that aims to automatically configure hosts, host groups, host inventories, template groups and templates in the monitoring software [Zabbix](https://www.zabbix.com/).
44

5-
Note: This is only tested with Zabbix 5.0 LTS.
5+
Note: Only tested with Zabbix 6.0 and 6.4.
66

77
## Requirements
88

@@ -101,7 +101,7 @@ def collect(*args: Any, **kwargs: Any) -> List[Host]:
101101
102102
if __name__ == "__main__":
103103
for host in collect():
104-
print(host.json())
104+
print(host.model_dump_json())
105105
EOF
106106
cat > path/to/host_modifier_dir/mod.py << EOF
107107
from zabbix_auto_config.models import Host

config.sample.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,18 @@ tags_prefix = "zac_"
4949
managed_inventory = ["location"]
5050

5151
# Names of hostgroups that zabbix-auto-config will manage.
52-
#hostgroup_all = "All-hosts"
53-
#hostgroup_manual = "All-manual-hosts"
54-
#hostgroup_disabled = "All-auto-disabled-hosts"
55-
#hostgroup_source_prefix = "Source-"
56-
#hostgroup_importance_prefix = "Importance-"
57-
#extra_siteadmin_hostgroup_prefixes = []
52+
hostgroup_all = "All-hosts"
53+
hostgroup_manual = "All-manual-hosts"
54+
hostgroup_disabled = "All-auto-disabled-hosts"
55+
hostgroup_source_prefix = "Source-"
56+
hostgroup_importance_prefix = "Importance-"
57+
58+
# Template group creation
59+
# NOTE: will create host groups if enabled on Zabbix <6.2
60+
create_templategroups = true
61+
templategroup_prefix = "Templates-"
62+
63+
extra_siteadmin_hostgroup_prefixes = []
5864

5965
[source_collectors.mysource]
6066
# Name of the source collector module without the .py extension
@@ -87,5 +93,5 @@ another_kwarg = "value2" # We can pass an arbitrary number of kwargs to
8793
module_name = "mysource"
8894
update_interval = 60
8995
error_tolerance = 0 # no tolerance for errors (default)
90-
exit_on_error = true # exit application if source fails
96+
exit_on_error = true # exit application if source fails (default)
9197
source = "other" # extra kwarg used in mysource module

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies = [
3131
"pyzabbix>=1.3.0",
3232
"requests>=1.0.0",
3333
"tomli>=2.0.0",
34+
"packaging>=23.2",
3435
]
3536

3637
[project.optional-dependencies]

tests/test_version.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Santiy testing of Zabbix API version parsing.
2+
3+
Tests against known versions of Zabbix. Expects support for alpha, beta and rc.
4+
"""
5+
6+
from typing import Tuple
7+
from packaging.version import Version
8+
import pytest
9+
10+
11+
@pytest.mark.parametrize(
12+
"version, release",
13+
[
14+
# Certain major versions released in 2023
15+
("7.0.0", (7, 0, 0)),
16+
("6.4.8", (6, 4, 8)),
17+
("6.0.23", (6, 0, 23)),
18+
("5.0.39", (5, 0, 39)),
19+
("6.2.9", (6, 2, 9)),
20+
# Pre-release versions
21+
("7.0.0alpha7", (7, 0, 0)),
22+
("7.0.0a7", (7, 0, 0)), # short form
23+
("6.4.0beta6", (6, 4, 0)),
24+
("6.4.0b6", (6, 4, 0)), # short form
25+
("6.4.8rc1", (6, 4, 8)),
26+
],
27+
)
28+
def test_version(version: str, release: Tuple[int, int, int]):
29+
"""Test that the version string is parsed correctly."""
30+
v = Version(version)
31+
assert v.release == release
32+
assert v.major == release[0]
33+
assert v.minor == release[1]
34+
assert v.micro == release[2]
35+
36+
# Test comparison
37+
assert v.release < (999, 999, 999)
38+
assert v.release > (0, 0, 0)
39+
assert v > Version("0.0.0")
40+
assert v < Version("999.999.999")

zabbix_auto_config/models.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import logging
24
from pathlib import Path
35
from typing import Any, Dict, List, Optional, Set, Tuple, Union
@@ -62,13 +64,16 @@ class ZabbixSettings(ConfigBaseModel):
6264

6365
hostgroup_source_prefix: str = "Source-"
6466
hostgroup_importance_prefix: str = "Importance-"
67+
68+
create_templategroups: bool = False
69+
templategroup_prefix: str = "Templates-"
6570

6671
# Prefixes for extra host groups to create based on the host groups
6772
# in the siteadmin mapping.
68-
# e.g. Siteadmin-foo -> Templates-foo if list is ["Templates-"]
73+
# e.g. Siteadmin-foo -> Secondary-foo if list is ["Secondary-"]
6974
# The groups must have prefixes separated by a hyphen (-) in order
7075
# to replace them with any of these prefixes.
71-
# These groups are not managed by ZAC beyond creating them.
76+
# These groups are not managed by ZAC beyond their creation.
7277
extra_siteadmin_hostgroup_prefixes: Set[str] = set()
7378

7479
@field_validator("timeout")

zabbix_auto_config/processing.py

Lines changed: 94 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
import signal
1414
import itertools
1515
import queue
16-
from typing import Dict, List, TYPE_CHECKING, Optional
16+
from typing import Dict, List, TYPE_CHECKING, Optional, Set
1717

18+
from packaging.version import Version
1819
import psycopg2
1920
from pydantic import ValidationError
2021
import pyzabbix
@@ -604,6 +605,9 @@ def __init__(self, name, state, db_uri, settings: models.Settings):
604605
os.path.join(self.config.map_dir, "siteadmin_hostgroup_map.txt")
605606
)
606607

608+
ver = self.api.apiinfo.version()
609+
self.zabbix_version = Version(ver)
610+
607611
def work(self):
608612
start_time = time.time()
609613
logging.info("Zabbix update starting")
@@ -983,8 +987,8 @@ def clear_templates(self, templates, host):
983987
logging.debug("DRYRUN: Clearing templates on host: '%s'", host["host"])
984988

985989
def set_templates(self, templates, host):
986-
logging.debug("Setting templates on host: '%s'", host["host"])
987990
if not self.config.dryrun:
991+
logging.debug("Setting templates on host: '%s'", host["host"])
988992
try:
989993
templates = [{"templateid": template_id} for _, template_id in templates.items()]
990994
self.api.host.update(hostid=host["hostid"], templates=templates)
@@ -1064,24 +1068,29 @@ def set_hostgroups(self, hostgroups, host):
10641068
else:
10651069
logging.debug("DRYRUN: Setting hostgroups on host: '%s'", host["host"])
10661070

1067-
def create_hostgroup(self, hostgroup_name):
1068-
if not self.config.dryrun:
1069-
logging.debug("Creating hostgroup: '%s'", hostgroup_name)
1070-
try:
1071-
result = self.api.hostgroup.create(name=hostgroup_name)
1072-
return result["groupids"][0]
1073-
except pyzabbix.ZabbixAPIException as e:
1074-
logging.error("Error when creating hostgroups '%s': %s", hostgroup_name, e.args)
1075-
else:
1071+
def create_hostgroup(self, hostgroup_name: str) -> Optional[str]:
1072+
if self.config.dryrun:
10761073
logging.debug("DRYRUN: Creating hostgroup: '%s'", hostgroup_name)
1077-
return "-1"
1074+
return None
1075+
1076+
logging.debug("Creating hostgroup: '%s'", hostgroup_name)
1077+
try:
1078+
result = self.api.hostgroup.create(name=hostgroup_name)
1079+
groupid = result["groupids"][0]
1080+
logging.info("Created host group '%s' (%s)", hostgroup_name, groupid)
1081+
return groupid
1082+
except pyzabbix.ZabbixAPIException as e:
1083+
logging.error(
1084+
"Error when creating hostgroups '%s': %s", hostgroup_name, e.args
1085+
)
1086+
return None
10781087

10791088
def create_extra_hostgroups(
10801089
self, existing_hostgroups: List[Dict[str, str]]
10811090
) -> None:
10821091
"""Creates additonal host groups based on the prefixes specified
10831092
in the config file. These host groups are not assigned hosts by ZAC."""
1084-
hostgroup_names = [h["name"] for h in existing_hostgroups]
1093+
hostgroup_names = set(h["name"] for h in existing_hostgroups)
10851094

10861095
for prefix in self.config.extra_siteadmin_hostgroup_prefixes:
10871096
mapping = utils.mapping_values_with_prefix(
@@ -1094,6 +1103,74 @@ def create_extra_hostgroups(
10941103
continue
10951104
self.create_hostgroup(hostgroup)
10961105

1106+
def create_templategroup(self, templategroup_name: str) -> Optional[str]:
1107+
if self.config.dryrun:
1108+
logging.debug("DRYRUN: Creating template group: '%s'", templategroup_name)
1109+
return None
1110+
1111+
logging.debug("Creating template group: '%s'", templategroup_name)
1112+
try:
1113+
result = self.api.templategroup.create(name=templategroup_name)
1114+
groupid = result["groupids"][0]
1115+
logging.info("Created template group '%s' (%s)", templategroup_name, groupid)
1116+
return groupid
1117+
except pyzabbix.ZabbixAPIException as e:
1118+
logging.error(
1119+
"Error when creating template group '%s': %s",
1120+
templategroup_name,
1121+
e.args,
1122+
)
1123+
return None
1124+
1125+
def create_templategroups(self, existing_hostgroups: List[Dict[str, str]]) -> None:
1126+
"""Creates template groups for each host group in the siteadmin
1127+
mapping file with the configured template group prefix.
1128+
1129+
For Zabbix <6.2, host groups are created instead of template groups."""
1130+
# Construct a set of all template group names from siteadmin mapping file
1131+
# by replacing the host group prefix with the template group prefix
1132+
tgroups = set(
1133+
utils.with_prefix(tg, self.config.templategroup_prefix)
1134+
for tg in itertools.chain.from_iterable(
1135+
self.siteadmin_hostgroup_map.values()
1136+
)
1137+
)
1138+
if self.zabbix_version.release >= (6, 2, 0):
1139+
logging.debug("Zabbix version is %s. Creating template groups.", self.zabbix_version)
1140+
self._create_templategroups(tgroups)
1141+
else:
1142+
logging.debug("Zabbix version is %s. Creating template groups as host groups.", self.zabbix_version)
1143+
self._create_templategroups_pre_62_compat(tgroups, existing_hostgroups)
1144+
1145+
1146+
def _create_templategroups(self, tgroups: Set[str]) -> None:
1147+
"""Create the given template groups if they don't exist.
1148+
1149+
Args:
1150+
tgroups: A set of template group names to create.
1151+
"""
1152+
res = self.api.templategroup.get(output=["name", "groupid"])
1153+
existing_tgroups = set(tg["name"] for tg in res)
1154+
for tgroup in tgroups:
1155+
if tgroup in existing_tgroups:
1156+
continue
1157+
self.create_templategroup(tgroup)
1158+
1159+
def _create_templategroups_pre_62_compat(self, tgroups: Set[str], existing_hostgroups: List[Dict[str, str]]) -> None:
1160+
"""Compatibility method for creating template groups on Zabbix <6.2.
1161+
1162+
Because template groups do not exist in <6.2, we instead create
1163+
host groups with the given names.
1164+
1165+
Args:
1166+
tgroups: A set of template group names to create.
1167+
"""
1168+
existing_hgroup_names = set(h["name"] for h in existing_hostgroups)
1169+
for tgroup in tgroups:
1170+
if tgroup in existing_hgroup_names:
1171+
continue
1172+
self.create_hostgroup(tgroup)
1173+
10971174
def do_update(self):
10981175
managed_hostgroup_names = set(
10991176
itertools.chain.from_iterable(self.property_hostgroup_map.values())
@@ -1107,6 +1184,10 @@ def do_update(self):
11071184
# Create extra host groups if necessary
11081185
if self.config.extra_siteadmin_hostgroup_prefixes:
11091186
self.create_extra_hostgroups(existing_hostgroups)
1187+
1188+
# Create template groups if enabled
1189+
if self.config.create_templategroups:
1190+
self.create_templategroups(existing_hostgroups)
11101191

11111192
zabbix_hostgroups = {}
11121193
for zabbix_hostgroup in existing_hostgroups:

0 commit comments

Comments
 (0)