first supoport of multiple hostgroups

This commit is contained in:
Raymond Kuiper 2025-06-04 14:23:01 +02:00
parent 6bdaf4e5b7
commit bc53737e02
5 changed files with 68 additions and 44 deletions

BIN
modules/.device.py.swp Normal file

Binary file not shown.

View File

@ -6,6 +6,7 @@ from copy import deepcopy
from logging import getLogger from logging import getLogger
from os import sys from os import sys
from re import search from re import search
from operator import itemgetter
from zabbix_utils import APIRequestError from zabbix_utils import APIRequestError
@ -64,11 +65,11 @@ class PhysicalDevice:
self.status = nb.status.label self.status = nb.status.label
self.zabbix = zabbix self.zabbix = zabbix
self.zabbix_id = None self.zabbix_id = None
self.group_id = None self.group_ids = []
self.nb_api_version = nb_version self.nb_api_version = nb_version
self.zbx_template_names = [] self.zbx_template_names = []
self.zbx_templates = [] self.zbx_templates = []
self.hostgroup = None self.hostgroups = []
self.tenant = nb.tenant self.tenant = nb.tenant
self.config_context = nb.config_context self.config_context = nb.config_context
self.zbxproxy = None self.zbxproxy = None
@ -152,7 +153,10 @@ class PhysicalDevice:
nb_regions=nb_regions, nb_regions=nb_regions,
) )
# Generate hostgroup based on hostgroup format # 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): def set_template(self, prefer_config_context, overrule_custom):
"""Set Template""" """Set Template"""
@ -333,12 +337,16 @@ class PhysicalDevice:
OUTPUT: True / False OUTPUT: True / False
""" """
# Go through all groups # Go through all groups
for group in groups: self.logger.debug(self.hostgroups)
if group["name"] == self.hostgroup:
self.group_id = group["groupid"] for hg in self.hostgroups:
e = f"Host {self.name}: matched group {group['name']}" for group in groups:
self.logger.debug(e) if group["name"] == hg:
return True 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 return False
def cleanup(self): def cleanup(self):
@ -514,7 +522,8 @@ class PhysicalDevice:
templateids.append({"templateid": template["templateid"]}) templateids.append({"templateid": template["templateid"]})
# Set interface, group and template configuration # Set interface, group and template configuration
interfaces = self.setInterfaceDetails() interfaces = self.setInterfaceDetails()
groups = [{"groupid": self.group_id}]
groups = self.group_ids
# Set Zabbix proxy if defined # Set Zabbix proxy if defined
self.setProxy(proxies) self.setProxy(proxies)
# Set basic data for host creation # Set basic data for host creation
@ -567,25 +576,26 @@ class PhysicalDevice:
""" """
final_data = [] final_data = []
# Check if the hostgroup is in a nested format and check each parent # Check if the hostgroup is in a nested format and check each parent
for pos in range(len(self.hostgroup.split("/"))): for hostgroup in self.hostgroups:
zabbix_hg = self.hostgroup.rsplit("/", pos)[0] for pos in range(len(hostgroup.split("/"))):
if self.lookupZabbixHostgroup(hostgroups, zabbix_hg): zabbix_hg = hostgroup.rsplit("/", pos)[0]
# Hostgroup already exists if self.lookupZabbixHostgroup(hostgroups, zabbix_hg):
continue # Hostgroup already exists
# Create new group continue
try: # Create new group
# API call to Zabbix try:
groupid = self.zabbix.hostgroup.create(name=zabbix_hg) # API call to Zabbix
e = f"Hostgroup '{zabbix_hg}': created in Zabbix." groupid = self.zabbix.hostgroup.create(name=zabbix_hg)
self.logger.info(e) e = f"Hostgroup '{zabbix_hg}': created in Zabbix."
# Add group to final data self.logger.info(e)
final_data.append( # Add group to final data
{"groupid": groupid["groupids"][0], "name": zabbix_hg} 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)}." except APIRequestError as e:
self.logger.error(msg) msg = f"Hostgroup '{zabbix_hg}': unable to create. Zabbix returned {str(e)}."
raise SyncExternalError(msg) from e self.logger.error(msg)
raise SyncExternalError(msg) from e
return final_data return final_data
def lookupZabbixHostgroup(self, group_list, lookup_group): def lookupZabbixHostgroup(self, group_list, lookup_group):
@ -625,7 +635,7 @@ class PhysicalDevice:
Checks if Zabbix object is still valid with NetBox parameters. Checks if Zabbix object is still valid with NetBox parameters.
""" """
# If group is found or if the hostgroup is nested # 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: if create_hostgroups:
# Script is allowed to create a new hostgroup # Script is allowed to create a new hostgroup
new_groups = self.createZabbixHostgroup(groups) new_groups = self.createZabbixHostgroup(groups)
@ -633,7 +643,7 @@ class PhysicalDevice:
# Add all new groups to the list of groups # Add all new groups to the list of groups
groups.append(group) groups.append(group)
# check if the initial group was not already found (and this is a nested folder check) # 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 # Function returns true / false but also sets GroupID
if not self.setZabbixGroupID(groups) and not create_hostgroups: if not self.setZabbixGroupID(groups) and not create_hostgroups:
e = ( e = (
@ -642,6 +652,9 @@ class PhysicalDevice:
) )
self.logger.warning(e) self.logger.warning(e)
raise SyncInventoryError(e) raise SyncInventoryError(e)
#if self.group_ids:
# self.group_ids.append(self.pri_group_id)
# Prepare templates and proxy config # Prepare templates and proxy config
self.zbxTemplatePrepper(templates) self.zbxTemplatePrepper(templates)
self.setProxy(proxies) self.setProxy(proxies)
@ -680,6 +693,7 @@ class PhysicalDevice:
f"Received value: {host['host']}" f"Received value: {host['host']}"
) )
self.updateZabbixHost(host=self.name) self.updateZabbixHost(host=self.name)
# Execute check depending on wether the name is special or not # Execute check depending on wether the name is special or not
if self.use_visible_name: if self.use_visible_name:
if host["name"] == self.visible_name: if host["name"] == self.visible_name:
@ -709,18 +723,20 @@ class PhysicalDevice:
group_dictname = "hostgroups" group_dictname = "hostgroups"
if str(self.zabbix.version).startswith(("6", "5")): if str(self.zabbix.version).startswith(("6", "5")):
group_dictname = "groups" group_dictname = "groups"
for group in host[group_dictname]: # Check if hostgroups match
if group["groupid"] == self.group_id: if (sorted(host[group_dictname], key=itemgetter('groupid')) ==
self.logger.debug(f"Host {self.name}: hostgroup in-sync.") sorted(self.group_ids, key=itemgetter('groupid'))):
break self.logger.debug(f"Host {self.name}: hostgroups in-sync.")
self.logger.warning(f"Host {self.name}: hostgroup OUT of sync.") else:
self.updateZabbixHost(groups={"groupid": self.group_id}) self.logger.warning(f"Host {self.name}: hostgroups OUT of sync.")
self.updateZabbixHost(groups=self.group_ids)
if int(host["status"]) == self.zabbix_state: if int(host["status"]) == self.zabbix_state:
self.logger.debug(f"Host {self.name}: status in-sync.") self.logger.debug(f"Host {self.name}: status in-sync.")
else: else:
self.logger.warning(f"Host {self.name}: status OUT of sync.") self.logger.warning(f"Host {self.name}: status OUT of sync.")
self.updateZabbixHost(status=str(self.zabbix_state)) self.updateZabbixHost(status=str(self.zabbix_state))
# Check if a proxy has been defined # Check if a proxy has been defined
if self.zbxproxy: if self.zbxproxy:
# Check if proxy or proxy group is defined # Check if proxy or proxy group is defined
@ -882,7 +898,7 @@ class PhysicalDevice:
e = ( e = (
f"Host {self.name} has unsupported interface configuration." f"Host {self.name} has unsupported interface configuration."
f" Host has total of {len(host['interfaces'])} interfaces. " f" Host has total of {len(host['interfaces'])} interfaces. "
"Manual interfention required." "Manual intervention required."
) )
self.logger.error(e) self.logger.error(e)
raise SyncInventoryError(e) raise SyncInventoryError(e)

View File

@ -91,7 +91,6 @@ class Hostgroup:
if self.nb.cluster: if self.nb.cluster:
format_options["cluster"] = self.nb.cluster.name format_options["cluster"] = self.nb.cluster.name
format_options["cluster_type"] = self.nb.cluster.type.name format_options["cluster_type"] = self.nb.cluster.type.name
self.format_options = format_options self.format_options = format_options
def set_nesting( def set_nesting(

View File

@ -58,7 +58,10 @@ class VirtualMachine(PhysicalDevice):
nb_regions=nb_regions, nb_regions=nb_regions,
) )
# Generate hostgroup based on hostgroup format # 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): def set_vm_template(self):
"""Set Template for VMs. Overwrites default class """Set Template for VMs. Overwrites default class

View File

@ -85,7 +85,13 @@ def main(arguments):
# Set NetBox API # Set NetBox API
netbox = api(netbox_host, token=netbox_token, threading=True) netbox = api(netbox_host, token=netbox_token, threading=True)
# Check if the provided Hostgroup layout is valid # 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 = [ allowed_objects = [
"location", "location",
"role", "role",
@ -116,7 +122,7 @@ def main(arguments):
if hg_object not in allowed_objects: if hg_object not in allowed_objects:
e = ( e = (
f"Hostgroup item {hg_object} is not valid. Make sure you" 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) logger.error(e)
raise HostgroupError(e) raise HostgroupError(e)
@ -215,7 +221,7 @@ def main(arguments):
create_hostgroups, create_hostgroups,
) )
continue continue
# Add hostgroup is config is set # Add hostgroup if config is set
if create_hostgroups: if create_hostgroups:
# Create new hostgroup. Potentially multiple groups if nested # Create new hostgroup. Potentially multiple groups if nested
hostgroups = vm.createZabbixHostgroup(zabbix_groups) hostgroups = vm.createZabbixHostgroup(zabbix_groups)
@ -243,7 +249,7 @@ def main(arguments):
continue continue
device.set_hostgroup(hostgroup_format, netbox_site_groups, netbox_regions) device.set_hostgroup(hostgroup_format, netbox_site_groups, netbox_regions)
# Check if a valid hostgroup has been found for this VM. # Check if a valid hostgroup has been found for this VM.
if not device.hostgroup: if not device.hostgroups:
continue continue
device.set_inventory(nb_device) device.set_inventory(nb_device)
device.set_usermacros() device.set_usermacros()