From 1b831a2d399d834a71796160bf9d6c475238d38a Mon Sep 17 00:00:00 2001 From: Raymond Kuiper Date: Fri, 14 Feb 2025 09:46:55 +0100 Subject: [PATCH] Moved Inventory mapping logic to tools module --- modules/device.py | 52 ++++++++++++++++++++++++----------------------- modules/tools.py | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/modules/device.py b/modules/device.py index 1a9e452..380bb56 100644 --- a/modules/device.py +++ b/modules/device.py @@ -13,6 +13,7 @@ from modules.exceptions import (SyncInventoryError, TemplateError, SyncExternalE from modules.interface import ZabbixInterface from modules.usermacros import ZabbixUsermacros from modules.hostgroups import Hostgroup +from modules.tools import field_mapper from pprint import pprint try: @@ -195,31 +196,32 @@ class PhysicalDevice(): self.inventory = {} if inventory_sync and self.inventory_mode in [0,1]: self.logger.debug(f"Host {self.name}: Starting inventory mapper") - # Let's build an inventory dict for each property in the inventory_map - for nb_inv_field, zbx_inv_field in self._inventory_map().items(): - field_list = nb_inv_field.split("/") # convert str to list based on delimiter - # start at the base of the dict... - value = nbdevice - # ... and step through the dict till we find the needed value - for item in field_list: - value = value[item] if value else None - # Check if the result is usable and expected - # We want to apply any int or float 0 values, - # even if python thinks those are empty. - if ((value and isinstance(value, int | float | str )) or - (isinstance(value, int | float) and int(value) ==0)): - self.inventory[zbx_inv_field] = str(value) - elif not value: - # empty value should just be an empty string for API compatibility - self.logger.debug(f"Host {self.name}: NetBox inventory lookup for " - f"'{nb_inv_field}' returned an empty value") - self.inventory[zbx_inv_field] = "" - else: - # Value is not a string or numeral, probably not what the user expected. - self.logger.error(f"Host {self.name}: Inventory lookup for '{nb_inv_field}'" - " returned an unexpected type: it will be skipped.") - self.logger.debug(f"Host {self.name}: Inventory mapping complete. " - f"Mapped {len(list(filter(None, self.inventory.values())))} field(s)") + self.inventory = field_mapper(self.name, self._inventory_map(), nbdevice, self.logger) +# # Let's build an inventory dict for each property in the inventory_map +# for nb_inv_field, zbx_inv_field in self._inventory_map().items(): +# field_list = nb_inv_field.split("/") # convert str to list based on delimiter +# # start at the base of the dict... +# value = nbdevice +# # ... and step through the dict till we find the needed value +# for item in field_list: +# value = value[item] if value else None +# # Check if the result is usable and expected +# # We want to apply any int or float 0 values, +# # even if python thinks those are empty. +# if ((value and isinstance(value, int | float | str )) or +# (isinstance(value, int | float) and int(value) ==0)): +# self.inventory[zbx_inv_field] = str(value) +# elif not value: +# # empty value should just be an empty string for API compatibility +# self.logger.debug(f"Host {self.name}: NetBox inventory lookup for " +# f"'{nb_inv_field}' returned an empty value") +# self.inventory[zbx_inv_field] = "" +# else: +# # Value is not a string or numeral, probably not what the user expected. +# self.logger.error(f"Host {self.name}: Inventory lookup for '{nb_inv_field}'" +# " returned an unexpected type: it will be skipped.") +# self.logger.debug(f"Host {self.name}: Inventory mapping complete. " +# f"Mapped {len(list(filter(None, self.inventory.values())))} field(s)") return True def isCluster(self): diff --git a/modules/tools.py b/modules/tools.py index f722524..1f197b6 100644 --- a/modules/tools.py +++ b/modules/tools.py @@ -1,3 +1,5 @@ +from logging import getLogger + """A collection of tools used by several classes""" def convert_recordset(recordset): """ Converts netbox RedcordSet to list of dicts. """ @@ -42,3 +44,37 @@ def proxy_prepper(proxy_list, proxy_group_list): group["monitored_by"] = 2 output.append(group) return output + + +def field_mapper(host, mapper, nbdevice, logger): + """ + Maps NetBox field data to Zabbix properties. + Used for Inventory, Usermacros and Tag mappings. + """ + data={} + # Let's build an dict for each property in the map + for nb_field, zbx_field in mapper.items(): + field_list = nb_field.split("/") # convert str to list based on delimiter + # start at the base of the dict... + value = nbdevice + # ... and step through the dict till we find the needed value + for item in field_list: + value = value[item] if value else None + # Check if the result is usable and expected + # We want to apply any int or float 0 values, + # even if python thinks those are empty. + if ((value and isinstance(value, int | float | str )) or + (isinstance(value, int | float) and int(value) ==0)): + data[zbx_field] = str(value) + elif not value: + # empty value should just be an empty string for API compatibility + logger.debug(f"Host {host}: NetBox lookup for " + f"'{nb_field}' returned an empty value") + data[zbx_field] = "" + else: + # Value is not a string or numeral, probably not what the user expected. + logger.error(f"Host {host}: Lookup for '{nb_field}'" + " returned an unexpected type: it will be skipped.") + logger.debug(f"Host {host}: Field mapping complete." + f"Mapped {len(list(filter(None, data.values())))} field(s)") + return data