From bc53737e02017a972cdbe01085128ce0a5ee7a29 Mon Sep 17 00:00:00 2001 From: Raymond Kuiper Date: Wed, 4 Jun 2025 14:23:01 +0200 Subject: [PATCH] first supoport of multiple hostgroups --- modules/.device.py.swp | Bin 0 -> 57344 bytes modules/device.py | 92 ++++++++++++++++++++++--------------- modules/hostgroups.py | 1 - modules/virtual_machine.py | 5 +- netbox_zabbix_sync.py | 14 ++++-- 5 files changed, 68 insertions(+), 44 deletions(-) create mode 100644 modules/.device.py.swp diff --git a/modules/.device.py.swp b/modules/.device.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..62a997b799a5c93df7d70bce7a66d87b8f631d7d GIT binary patch literal 57344 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 Z|L$^DwaNs|-y3Bb{p7Y)eG+A|`M literal 0 HcmV?d00001 diff --git a/modules/device.py b/modules/device.py index b8d038d..0d9bca9 100644 --- a/modules/device.py +++ b/modules/device.py @@ -6,6 +6,7 @@ from copy import deepcopy from logging import getLogger from os import sys from re import search +from operator import itemgetter from zabbix_utils import APIRequestError @@ -64,11 +65,11 @@ class PhysicalDevice: self.status = nb.status.label self.zabbix = zabbix self.zabbix_id = None - self.group_id = None + self.group_ids = [] self.nb_api_version = nb_version self.zbx_template_names = [] self.zbx_templates = [] - self.hostgroup = None + self.hostgroups = [] self.tenant = nb.tenant self.config_context = nb.config_context self.zbxproxy = None @@ -152,7 +153,10 @@ class PhysicalDevice: nb_regions=nb_regions, ) # Generate hostgroup based on hostgroup format - self.hostgroup = hg.generate(hg_format) + if isinstance(hg_format, list): + self.hostgroups = [hg.generate(f) for f in hg_format] + else: + self.hostgroups.append(hg.generate(hg_format)) def set_template(self, prefer_config_context, overrule_custom): """Set Template""" @@ -333,12 +337,16 @@ class PhysicalDevice: OUTPUT: True / False """ # Go through all groups - for group in groups: - if group["name"] == self.hostgroup: - self.group_id = group["groupid"] - e = f"Host {self.name}: matched group {group['name']}" - self.logger.debug(e) - return True + self.logger.debug(self.hostgroups) + + for hg in self.hostgroups: + for group in groups: + if group["name"] == hg: + self.group_ids.append({"groupid": group["groupid"]}) + e = f"Host {self.name}: matched group {group['name']}" + self.logger.debug(e) + if self.group_ids: + return True return False def cleanup(self): @@ -514,7 +522,8 @@ class PhysicalDevice: templateids.append({"templateid": template["templateid"]}) # Set interface, group and template configuration interfaces = self.setInterfaceDetails() - groups = [{"groupid": self.group_id}] + + groups = self.group_ids # Set Zabbix proxy if defined self.setProxy(proxies) # Set basic data for host creation @@ -567,25 +576,26 @@ class PhysicalDevice: """ final_data = [] # Check if the hostgroup is in a nested format and check each parent - for pos in range(len(self.hostgroup.split("/"))): - zabbix_hg = self.hostgroup.rsplit("/", pos)[0] - if self.lookupZabbixHostgroup(hostgroups, zabbix_hg): - # Hostgroup already exists - continue - # Create new group - try: - # API call to Zabbix - groupid = self.zabbix.hostgroup.create(name=zabbix_hg) - e = f"Hostgroup '{zabbix_hg}': created in Zabbix." - self.logger.info(e) - # Add group to final data - final_data.append( - {"groupid": groupid["groupids"][0], "name": zabbix_hg} - ) - except APIRequestError as e: - msg = f"Hostgroup '{zabbix_hg}': unable to create. Zabbix returned {str(e)}." - self.logger.error(msg) - raise SyncExternalError(msg) from e + for hostgroup in self.hostgroups: + for pos in range(len(hostgroup.split("/"))): + zabbix_hg = hostgroup.rsplit("/", pos)[0] + if self.lookupZabbixHostgroup(hostgroups, zabbix_hg): + # Hostgroup already exists + continue + # Create new group + try: + # API call to Zabbix + groupid = self.zabbix.hostgroup.create(name=zabbix_hg) + e = f"Hostgroup '{zabbix_hg}': created in Zabbix." + self.logger.info(e) + # Add group to final data + final_data.append( + {"groupid": groupid["groupids"][0], "name": zabbix_hg} + ) + except APIRequestError as e: + msg = f"Hostgroup '{zabbix_hg}': unable to create. Zabbix returned {str(e)}." + self.logger.error(msg) + raise SyncExternalError(msg) from e return final_data def lookupZabbixHostgroup(self, group_list, lookup_group): @@ -625,7 +635,7 @@ class PhysicalDevice: Checks if Zabbix object is still valid with NetBox parameters. """ # If group is found or if the hostgroup is nested - if not self.setZabbixGroupID(groups) or len(self.hostgroup.split("/")) > 1: + if not self.setZabbixGroupID(groups): # or len(self.hostgroups.split("/")) > 1: if create_hostgroups: # Script is allowed to create a new hostgroup new_groups = self.createZabbixHostgroup(groups) @@ -633,7 +643,7 @@ class PhysicalDevice: # Add all new groups to the list of groups groups.append(group) # check if the initial group was not already found (and this is a nested folder check) - if not self.group_id: + if not self.group_ids: # Function returns true / false but also sets GroupID if not self.setZabbixGroupID(groups) and not create_hostgroups: e = ( @@ -642,6 +652,9 @@ class PhysicalDevice: ) self.logger.warning(e) raise SyncInventoryError(e) + #if self.group_ids: + # self.group_ids.append(self.pri_group_id) + # Prepare templates and proxy config self.zbxTemplatePrepper(templates) self.setProxy(proxies) @@ -680,6 +693,7 @@ class PhysicalDevice: f"Received value: {host['host']}" ) self.updateZabbixHost(host=self.name) + # Execute check depending on wether the name is special or not if self.use_visible_name: if host["name"] == self.visible_name: @@ -709,18 +723,20 @@ class PhysicalDevice: group_dictname = "hostgroups" if str(self.zabbix.version).startswith(("6", "5")): group_dictname = "groups" - for group in host[group_dictname]: - if group["groupid"] == self.group_id: - self.logger.debug(f"Host {self.name}: hostgroup in-sync.") - break - self.logger.warning(f"Host {self.name}: hostgroup OUT of sync.") - self.updateZabbixHost(groups={"groupid": self.group_id}) + # Check if hostgroups match + if (sorted(host[group_dictname], key=itemgetter('groupid')) == + sorted(self.group_ids, key=itemgetter('groupid'))): + self.logger.debug(f"Host {self.name}: hostgroups in-sync.") + else: + self.logger.warning(f"Host {self.name}: hostgroups OUT of sync.") + self.updateZabbixHost(groups=self.group_ids) if int(host["status"]) == self.zabbix_state: self.logger.debug(f"Host {self.name}: status in-sync.") else: self.logger.warning(f"Host {self.name}: status OUT of sync.") self.updateZabbixHost(status=str(self.zabbix_state)) + # Check if a proxy has been defined if self.zbxproxy: # Check if proxy or proxy group is defined @@ -882,7 +898,7 @@ class PhysicalDevice: e = ( f"Host {self.name} has unsupported interface configuration." f" Host has total of {len(host['interfaces'])} interfaces. " - "Manual interfention required." + "Manual intervention required." ) self.logger.error(e) raise SyncInventoryError(e) diff --git a/modules/hostgroups.py b/modules/hostgroups.py index d1350bd..68b0bb1 100644 --- a/modules/hostgroups.py +++ b/modules/hostgroups.py @@ -91,7 +91,6 @@ class Hostgroup: if self.nb.cluster: format_options["cluster"] = self.nb.cluster.name format_options["cluster_type"] = self.nb.cluster.type.name - self.format_options = format_options def set_nesting( diff --git a/modules/virtual_machine.py b/modules/virtual_machine.py index 34e3394..8915832 100644 --- a/modules/virtual_machine.py +++ b/modules/virtual_machine.py @@ -58,7 +58,10 @@ class VirtualMachine(PhysicalDevice): nb_regions=nb_regions, ) # Generate hostgroup based on hostgroup format - self.hostgroup = hg.generate(hg_format) + if isinstance(hg_format, list): + self.hostgroups = [hg.generate(f) for f in hg_format] + else: + self.hostgroups.append(hg.generate(hg_format)) def set_vm_template(self): """Set Template for VMs. Overwrites default class diff --git a/netbox_zabbix_sync.py b/netbox_zabbix_sync.py index 79d8a20..08e3036 100755 --- a/netbox_zabbix_sync.py +++ b/netbox_zabbix_sync.py @@ -85,7 +85,13 @@ def main(arguments): # Set NetBox API netbox = api(netbox_host, token=netbox_token, threading=True) # Check if the provided Hostgroup layout is valid - hg_objects = hostgroup_format.split("/") + hg_objects = [] + if isinstance(hostgroup_format,list): + for l in hostgroup_format: + hg_objects = hg_objects + l.split("/") + else: + hg_objects = hostgroup_format.split("/") + hg_objects = sorted(set(hg_objects)) allowed_objects = [ "location", "role", @@ -116,7 +122,7 @@ def main(arguments): if hg_object not in allowed_objects: e = ( f"Hostgroup item {hg_object} is not valid. Make sure you" - " use valid items and seperate them with '/'." + " use valid items and separate them with '/'." ) logger.error(e) raise HostgroupError(e) @@ -215,7 +221,7 @@ def main(arguments): create_hostgroups, ) continue - # Add hostgroup is config is set + # Add hostgroup if config is set if create_hostgroups: # Create new hostgroup. Potentially multiple groups if nested hostgroups = vm.createZabbixHostgroup(zabbix_groups) @@ -243,7 +249,7 @@ def main(arguments): continue device.set_hostgroup(hostgroup_format, netbox_site_groups, netbox_regions) # Check if a valid hostgroup has been found for this VM. - if not device.hostgroup: + if not device.hostgroups: continue device.set_inventory(nb_device) device.set_usermacros()