reverted module split, switched to class inheretance instead. Updated config example.

This commit is contained in:
Raymond Kuiper 2025-02-12 12:30:28 +01:00
parent ba2f77a640
commit c7d3dab27c
4 changed files with 87 additions and 151 deletions

View File

@ -80,10 +80,10 @@ inventory_sync = False
# For nested properties, you can use the '/' seperator. # For nested properties, you can use the '/' seperator.
# For example, the following map will assign the custom field 'mycustomfield' to the 'alias' Zabbix inventory field: # For example, the following map will assign the custom field 'mycustomfield' to the 'alias' Zabbix inventory field:
# #
# inventory_map = { "custom_fields/mycustomfield/name": "alias"} # device_inventory_map = { "custom_fields/mycustomfield/name": "alias"}
# #
# The following map should provide some nice defaults: # The following maps should provide some nice defaults:
inventory_map = { "asset_tag": "asset_tag", device_inventory_map = { "asset_tag": "asset_tag",
"virtual_chassis/name": "chassis", "virtual_chassis/name": "chassis",
"status/label": "deployment_status", "status/label": "deployment_status",
"location/name": "location", "location/name": "location",
@ -96,3 +96,12 @@ inventory_map = { "asset_tag": "asset_tag",
"device_type/model": "type", "device_type/model": "type",
"device_type/manufacturer/name": "vendor", "device_type/manufacturer/name": "vendor",
"oob_ip/address": "oob_ip" } "oob_ip/address": "oob_ip" }
# We also support inventory mapping on Virtual Machines.
vm_inventory_map = { "asset_tag": "asset_tag",
"status/label": "deployment_status",
"location/name": "location",
"latitude": "location_lat",
"longitude": "location_lon",
"comments": "notes",
"name": "name" }

View File

@ -11,7 +11,6 @@ from modules.exceptions import (SyncInventoryError, TemplateError, SyncExternalE
InterfaceConfigError, JournalError) InterfaceConfigError, JournalError)
from modules.interface import ZabbixInterface from modules.interface import ZabbixInterface
from modules.hostgroups import Hostgroup from modules.hostgroups import Hostgroup
from modules.inventory import Inventory
try: try:
from config import ( from config import (
@ -19,6 +18,7 @@ try:
traverse_site_groups, traverse_site_groups,
traverse_regions, traverse_regions,
inventory_sync, inventory_sync,
inventory_mode,
device_inventory_map device_inventory_map
) )
except ModuleNotFoundError: except ModuleNotFoundError:
@ -63,6 +63,10 @@ class PhysicalDevice():
def __str__(self): def __str__(self):
return self.__repr__() return self.__repr__()
def _inventory_map(self):
""" Use device inventory maps """
return device_inventory_map
def _setBasics(self): def _setBasics(self):
""" """
Sets basic information like IP address. Sets basic information like IP address.
@ -162,56 +166,56 @@ class PhysicalDevice():
return [self.config_context["zabbix"]["templates"]] return [self.config_context["zabbix"]["templates"]]
return self.config_context["zabbix"]["templates"] return self.config_context["zabbix"]["templates"]
def set_inventory(self, nbdevice):
""" Set inventory """
Inventory.set_inventory(self, nbdevice)
# def set_inventory(self, nbdevice): # def set_inventory(self, nbdevice):
# """ Set host inventory """ # """ Set inventory """
# # Set inventory mode. Default is disabled (see class init function). # Inventory.set_inventory(self, nbdevice)
# if inventory_mode == "disabled":
# if inventory_sync: def set_inventory(self, nbdevice):
# self.logger.error(f"Host {self.name}: Unable to map NetBox inventory to Zabbix. " """ Set host inventory """
# "Inventory sync is enabled in config but inventory mode is disabled.") # Set inventory mode. Default is disabled (see class init function).
# return True if inventory_mode == "disabled":
# if inventory_mode == "manual": if inventory_sync:
# self.inventory_mode = 0 self.logger.error(f"Host {self.name}: Unable to map NetBox inventory to Zabbix. "
# elif inventory_mode == "automatic": "Inventory sync is enabled in config but inventory mode is disabled.")
# self.inventory_mode = 1 return True
# else: if inventory_mode == "manual":
# self.logger.error(f"Host {self.name}: Specified value for inventory mode in" self.inventory_mode = 0
# f" config is not valid. Got value {inventory_mode}") elif inventory_mode == "automatic":
# return False self.inventory_mode = 1
# self.inventory = {} else:
# if inventory_sync and self.inventory_mode in [0,1]: self.logger.error(f"Host {self.name}: Specified value for inventory mode in"
# self.logger.debug(f"Host {self.name}: Starting inventory mapper") f" config is not valid. Got value {inventory_mode}")
# # Let's build an inventory dict for each property in the inventory_map return False
# for nb_inv_field, zbx_inv_field in inventory_map.items(): self.inventory = {}
# field_list = nb_inv_field.split("/") # convert str to list based on delimiter if inventory_sync and self.inventory_mode in [0,1]:
# # start at the base of the dict... self.logger.debug(f"Host {self.name}: Starting inventory mapper")
# value = nbdevice # Let's build an inventory dict for each property in the inventory_map
# # ... and step through the dict till we find the needed value for nb_inv_field, zbx_inv_field in self._inventory_map().items():
# for item in field_list: field_list = nb_inv_field.split("/") # convert str to list based on delimiter
# value = value[item] if value else None # start at the base of the dict...
# # Check if the result is usable and expected value = nbdevice
# # We want to apply any int or float 0 values, # ... and step through the dict till we find the needed value
# # even if python thinks those are empty. for item in field_list:
# if ((value and isinstance(value, int | float | str )) or value = value[item] if value else None
# (isinstance(value, int | float) and int(value) ==0)): # Check if the result is usable and expected
# self.inventory[zbx_inv_field] = str(value) # We want to apply any int or float 0 values,
# elif not value: # even if python thinks those are empty.
# # empty value should just be an empty string for API compatibility if ((value and isinstance(value, int | float | str )) or
# self.logger.debug(f"Host {self.name}: NetBox inventory lookup for " (isinstance(value, int | float) and int(value) ==0)):
# f"'{nb_inv_field}' returned an empty value") self.inventory[zbx_inv_field] = str(value)
# self.inventory[zbx_inv_field] = "" elif not value:
# else: # empty value should just be an empty string for API compatibility
# # Value is not a string or numeral, probably not what the user expected. self.logger.debug(f"Host {self.name}: NetBox inventory lookup for "
# self.logger.error(f"Host {self.name}: Inventory lookup for '{nb_inv_field}'" f"'{nb_inv_field}' returned an empty value")
# " returned an unexpected type: it will be skipped.") self.inventory[zbx_inv_field] = ""
# self.logger.debug(f"Host {self.name}: Inventory mapping complete. " else:
# f"Mapped {len(list(filter(None, self.inventory.values())))} field(s)") # Value is not a string or numeral, probably not what the user expected.
# return True 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): def isCluster(self):
""" """
Checks if device is part of cluster. Checks if device is part of cluster.
@ -546,7 +550,7 @@ class PhysicalDevice():
'interfaceid'], 'interfaceid'],
selectGroups=["groupid"], selectGroups=["groupid"],
selectParentTemplates=["templateid"], selectParentTemplates=["templateid"],
selectInventory=list(device_inventory_map.values())) selectInventory=list(self._inventory_map().values()))
if len(host) > 1: if len(host) > 1:
e = (f"Got {len(host)} results for Zabbix hosts " e = (f"Got {len(host)} results for Zabbix hosts "
f"with ID {self.zabbix_id} - hostname {self.name}.") f"with ID {self.zabbix_id} - hostname {self.name}.")

View File

@ -1,81 +0,0 @@
#!/usr/bin/env python3
# pylint: disable=invalid-name, logging-not-lazy, too-many-locals, logging-fstring-interpolation, too-many-lines
"""
Device specific handeling for NetBox to Zabbix
"""
from pprint import pprint
from logging import getLogger
from zabbix_utils import APIRequestError
from modules.exceptions import (SyncInventoryError, TemplateError, SyncExternalError,
InterfaceConfigError, JournalError)
try:
from config import (
inventory_sync,
inventory_mode,
device_inventory_map,
vm_inventory_map
)
except ModuleNotFoundError:
print("Configuration file config.py not found in main directory."
"Please create the file or rename the config.py.example file to config.py.")
sys.exit(0)
class Inventory():
# pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-positional-arguments
"""
Represents Network device.
INPUT: (NetBox device class, ZabbixAPI class, journal flag, NB journal class)
"""
# def __init__(self, nb, logger=None):
# self.nb = nb
def set_inventory(self, nbobject):
if hasattr(nbobject, 'device_type'):
inventory_map = device_inventory_map
else:
inventory_map = vm_inventory_map
""" Set host inventory """
# Set inventory mode. Default is disabled (see class init function).
if inventory_mode == "disabled":
if inventory_sync:
self.logger.error(f"Host {self.name}: Unable to map NetBox inventory to Zabbix. "
"Inventory sync is enabled in config but inventory mode is disabled.")
return True
if inventory_mode == "manual":
self.inventory_mode = 0
elif inventory_mode == "automatic":
self.inventory_mode = 1
else:
self.logger.error(f"Host {self.name}: Specified value for inventory mode in"
f" config is not valid. Got value {inventory_mode}")
return False
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 inventory_map.items():
field_list = nb_inv_field.split("/") # convert str to list based on delimiter
# start at the base of the dict...
value = nbobject
# ... 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

View File

@ -6,11 +6,11 @@ from os import sys
from modules.device import PhysicalDevice from modules.device import PhysicalDevice
from modules.hostgroups import Hostgroup from modules.hostgroups import Hostgroup
from modules.interface import ZabbixInterface from modules.interface import ZabbixInterface
from modules.inventory import Inventory
from modules.exceptions import TemplateError, InterfaceConfigError, SyncInventoryError from modules.exceptions import TemplateError, InterfaceConfigError, SyncInventoryError
try: try:
from config import ( from config import (
inventory_sync, inventory_sync,
inventory_mode,
vm_inventory_map, vm_inventory_map,
traverse_site_groups, traverse_site_groups,
traverse_regions traverse_regions
@ -27,6 +27,10 @@ class VirtualMachine(PhysicalDevice):
self.hostgroup = None self.hostgroup = None
self.zbx_template_names = None self.zbx_template_names = None
def _inventory_map(self):
""" use VM inventory maps """
return vm_inventory_map
def set_hostgroup(self, hg_format, nb_site_groups, nb_regions): def set_hostgroup(self, hg_format, nb_site_groups, nb_regions):
"""Set the hostgroup for this device""" """Set the hostgroup for this device"""
# Create new Hostgroup instance # Create new Hostgroup instance
@ -38,9 +42,9 @@ class VirtualMachine(PhysicalDevice):
# Generate hostgroup based on hostgroup format # Generate hostgroup based on hostgroup format
self.hostgroup = hg.generate(hg_format) self.hostgroup = hg.generate(hg_format)
def set_inventory(self, nbvm): # def set_inventory(self, nbvm):
""" Set inventory """ # """ Set inventory """
Inventory.set_inventory(self, nbvm) # Inventory.set_inventory(self, nbvm)
def set_vm_template(self): def set_vm_template(self):
""" Set Template for VMs. Overwrites default class """ Set Template for VMs. Overwrites default class