mirror of
https://github.com/TheNetworkGuy/netbox-zabbix-sync.git
synced 2025-07-12 23:14:48 -06:00
Added tag support
This commit is contained in:
parent
fd70045c6d
commit
d65fa5b699
@ -121,3 +121,33 @@ vm_usermacro_map = {"memory": "{$TOTAL_MEMORY}",
|
||||
"url": "{$NB_URL}",
|
||||
"id": "{$NB_ID}"}
|
||||
|
||||
# To sync host tags to Zabbix, set to True.
|
||||
tag_sync = False
|
||||
|
||||
# Setting tag_lower to True will lower capital letters ain tag names and values
|
||||
# This is more inline with the Zabbix way of working with tags.
|
||||
#
|
||||
# You can however set this to False to ensure capital letters are synced to Zabbix tags.
|
||||
tag_lower = True
|
||||
|
||||
# We can sync NetBox device/VM tags to Zabbix, but as NetBox tags don't follow the key/value
|
||||
# pattern, we need to specify a tag name to register the NetBox tags in Zabbix.
|
||||
#
|
||||
#
|
||||
#
|
||||
# If tag_name is set to False, we won't sync NetBox device/VM tags to Zabbix.
|
||||
tag_name = 'NetBox'
|
||||
|
||||
# We can choose to use 'name', 'slug' or 'display' NetBox tag properties as a value in Zabbix.
|
||||
# 'name'is used by default.
|
||||
tag_value = "name"
|
||||
|
||||
# device tag_map to map NetBox fields to host tags.
|
||||
device_tag_map = {"site/name": "site",
|
||||
"rack/name": "rack",
|
||||
"platform/name": "target"}
|
||||
|
||||
# Virtual machine tag_map to map NetBox fields to host tags.
|
||||
vm_tag_map = {"site/name": "site",
|
||||
"cluster/name": "cluster",
|
||||
"platform/name": "target"}
|
||||
|
@ -12,8 +12,9 @@ from modules.exceptions import (SyncInventoryError, TemplateError, SyncExternalE
|
||||
InterfaceConfigError, JournalError)
|
||||
from modules.interface import ZabbixInterface
|
||||
from modules.usermacros import ZabbixUsermacros
|
||||
from modules.tags import ZabbixTags
|
||||
from modules.hostgroups import Hostgroup
|
||||
from modules.tools import field_mapper
|
||||
from modules.tools import field_mapper, remove_duplicates
|
||||
|
||||
try:
|
||||
from config import (
|
||||
@ -24,7 +25,12 @@ try:
|
||||
inventory_mode,
|
||||
device_inventory_map,
|
||||
usermacro_sync,
|
||||
device_usermacro_map
|
||||
device_usermacro_map,
|
||||
tag_sync,
|
||||
tag_lower,
|
||||
tag_name,
|
||||
tag_value,
|
||||
device_tag_map
|
||||
)
|
||||
except ModuleNotFoundError:
|
||||
print("Configuration file config.py not found in main directory."
|
||||
@ -60,6 +66,7 @@ class PhysicalDevice():
|
||||
self.inventory_mode = -1
|
||||
self.inventory = {}
|
||||
self.usermacros = {}
|
||||
self.tags = {}
|
||||
self.logger = logger if logger else getLogger(__name__)
|
||||
self._setBasics()
|
||||
|
||||
@ -77,6 +84,10 @@ class PhysicalDevice():
|
||||
""" Use device inventory maps """
|
||||
return device_usermacro_map
|
||||
|
||||
def _tag_map(self):
|
||||
""" Use device host tag maps """
|
||||
return device_tag_map
|
||||
|
||||
def _setBasics(self):
|
||||
"""
|
||||
Sets basic information like IP address.
|
||||
@ -362,6 +373,21 @@ class PhysicalDevice():
|
||||
self.usermacros = macros.generate()
|
||||
return True
|
||||
|
||||
|
||||
def set_tags(self):
|
||||
"""
|
||||
Generates Host Tags
|
||||
"""
|
||||
tags = ZabbixTags(self.nb, self._tag_map(),
|
||||
tag_sync, tag_lower, tag_name=tag_name,
|
||||
tag_value=tag_value, logger=self.logger,
|
||||
host=self.name)
|
||||
if tags.sync is False:
|
||||
self.tags = []
|
||||
|
||||
self.tags = tags.generate()
|
||||
return True
|
||||
|
||||
def setProxy(self, proxy_list):
|
||||
"""
|
||||
Sets proxy or proxy group if this
|
||||
@ -432,7 +458,8 @@ class PhysicalDevice():
|
||||
"description": description,
|
||||
"inventory_mode": self.inventory_mode,
|
||||
"inventory": self.inventory,
|
||||
"macros": self.usermacros
|
||||
"macros": self.usermacros,
|
||||
"tags": self.tags
|
||||
}
|
||||
# If a Zabbix proxy or Zabbix Proxy group has been defined
|
||||
if self.zbxproxy:
|
||||
@ -548,7 +575,8 @@ class PhysicalDevice():
|
||||
selectHostGroups=["groupid"],
|
||||
selectParentTemplates=["templateid"],
|
||||
selectInventory=list(self._inventory_map().values()),
|
||||
selectMacros=["macro","value","type","description"]
|
||||
selectMacros=["macro","value","type","description"],
|
||||
selectTags=["tag","value"]
|
||||
)
|
||||
if len(host) > 1:
|
||||
e = (f"Got {len(host)} results for Zabbix hosts "
|
||||
@ -598,9 +626,8 @@ class PhysicalDevice():
|
||||
if group["groupid"] == self.group_id:
|
||||
self.logger.debug(f"Host {self.name}: hostgroup in-sync.")
|
||||
break
|
||||
else:
|
||||
self.logger.warning(f"Host {self.name}: hostgroup OUT of sync.")
|
||||
self.updateZabbixHost(groups={'groupid': self.group_id})
|
||||
self.logger.warning(f"Host {self.name}: hostgroup OUT of sync.")
|
||||
self.updateZabbixHost(groups={'groupid': self.group_id})
|
||||
|
||||
if int(host["status"]) == self.zabbix_state:
|
||||
self.logger.debug(f"Host {self.name}: status in-sync.")
|
||||
@ -688,6 +715,14 @@ class PhysicalDevice():
|
||||
self.logger.warning(f"Host {self.name}: usermacros OUT of sync.")
|
||||
self.updateZabbixHost(macros=self.usermacros)
|
||||
|
||||
# Check host usermacros
|
||||
if tag_sync:
|
||||
if remove_duplicates(host['tags'],sortkey='tag') == self.tags:
|
||||
self.logger.debug(f"Host {self.name}: tags in-sync.")
|
||||
else:
|
||||
self.logger.warning(f"Host {self.name}: tags OUT of sync.")
|
||||
self.updateZabbixHost(tags=self.tags)
|
||||
|
||||
# If only 1 interface has been found
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
if len(host['interfaces']) == 1:
|
||||
|
117
modules/tags.py
Normal file
117
modules/tags.py
Normal file
@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-positional-arguments, logging-fstring-interpolation
|
||||
"""
|
||||
All of the Zabbix Usermacro related configuration
|
||||
"""
|
||||
from logging import getLogger
|
||||
from modules.tools import field_mapper, remove_duplicates
|
||||
|
||||
class ZabbixTags():
|
||||
"""Class that represents a Zabbix interface."""
|
||||
|
||||
def __init__(self, nb, tag_map, tag_sync, tag_lower=True,
|
||||
tag_name=None, tag_value=None, logger=None, host=None):
|
||||
self.nb = nb
|
||||
self.name = host if host else nb.name
|
||||
self.tag_map = tag_map
|
||||
self.logger = logger if logger else getLogger(__name__)
|
||||
self.tags = {}
|
||||
self.lower = tag_lower
|
||||
self.tag_name = tag_name
|
||||
self.tag_value = tag_value
|
||||
self.tag_sync = tag_sync
|
||||
self.sync = False
|
||||
self._set_config()
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def _set_config(self):
|
||||
"""
|
||||
Setup class
|
||||
"""
|
||||
if self.tag_sync:
|
||||
self.sync = True
|
||||
|
||||
return True
|
||||
|
||||
def validate_tag(self, tag_name):
|
||||
"""
|
||||
Validates tag name
|
||||
"""
|
||||
if tag_name and isinstance(tag_name, str) and len(tag_name)<=256:
|
||||
return True
|
||||
return False
|
||||
|
||||
def validate_value(self, tag_value):
|
||||
"""
|
||||
Validates tag value
|
||||
"""
|
||||
if tag_value and isinstance(tag_value, str) and len(tag_value)<=256:
|
||||
return True
|
||||
return False
|
||||
|
||||
def render_tag(self, tag_name, tag_value):
|
||||
"""
|
||||
Renders a tag
|
||||
"""
|
||||
tag={}
|
||||
if self.validate_tag(tag_name):
|
||||
if self.lower:
|
||||
tag['tag'] = tag_name.lower()
|
||||
else:
|
||||
tag['tag'] = tag_name
|
||||
else:
|
||||
self.logger.error(f'Tag {tag_name} is not a valid tag name, skipping.')
|
||||
return False
|
||||
|
||||
if self.validate_value(tag_value):
|
||||
if self.lower:
|
||||
tag['value'] = tag_value.lower()
|
||||
else:
|
||||
tag['value'] = tag_value
|
||||
else:
|
||||
self.logger.error(f'Tag {tag_name} has an invalid value: \'{tag_value}\', skipping.')
|
||||
return False
|
||||
return tag
|
||||
|
||||
def generate(self):
|
||||
"""
|
||||
Generate full set of Usermacros
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
tags=[]
|
||||
# Parse the field mapper for tags
|
||||
if self.tag_map:
|
||||
self.logger.debug(f"Host {self.nb.name}: Starting tag mapper")
|
||||
field_tags = field_mapper(self.nb.name, self.tag_map, self.nb, self.logger)
|
||||
for tag, value in field_tags.items():
|
||||
t = self.render_tag(tag, value)
|
||||
if t:
|
||||
tags.append(t)
|
||||
|
||||
# Parse NetBox config context for tags
|
||||
if ("zabbix" in self.nb.config_context and "tags" in self.nb.config_context['zabbix']
|
||||
and isinstance(self.nb.config_context['zabbix']['tags'], list)):
|
||||
for tag in self.nb.config_context['zabbix']['tags']:
|
||||
if isinstance(tag, dict):
|
||||
for tagname, value in tag.items():
|
||||
t = self.render_tag(tagname, value)
|
||||
if t:
|
||||
tags.append(t)
|
||||
|
||||
# Pull in NetBox device tags if tag_name is set
|
||||
if self.tag_name and isinstance(self.tag_name, str):
|
||||
for tag in self.nb.tags:
|
||||
if self.tag_value.lower() in ['display', 'name', 'slug']:
|
||||
value = tag[self.tag_value]
|
||||
else:
|
||||
value = tag['name']
|
||||
t = self.render_tag(self.tag_name, value)
|
||||
if t:
|
||||
tags.append(t)
|
||||
|
||||
return remove_duplicates(tags, sortkey='tag')
|
@ -76,3 +76,14 @@ def field_mapper(host, mapper, nbdevice, logger):
|
||||
logger.debug(f"Host {host}: Field mapping complete. "
|
||||
f"Mapped {len(list(filter(None, data.values())))} field(s)")
|
||||
return data
|
||||
|
||||
def remove_duplicates(input_list, sortkey=None):
|
||||
"""
|
||||
Removes duplicate entries from a list and sorts the list
|
||||
"""
|
||||
output_list = []
|
||||
if isinstance(input_list, list):
|
||||
output_list = [dict(t) for t in {tuple(d.items()) for d in input_list}]
|
||||
if sortkey and isinstance(sortkey, str):
|
||||
output_list.sort(key=lambda x: x[sortkey])
|
||||
return output_list
|
||||
|
@ -11,6 +11,7 @@ try:
|
||||
from config import (
|
||||
vm_inventory_map,
|
||||
vm_usermacro_map,
|
||||
vm_tag_map,
|
||||
traverse_site_groups,
|
||||
traverse_regions
|
||||
)
|
||||
@ -31,9 +32,13 @@ class VirtualMachine(PhysicalDevice):
|
||||
return vm_inventory_map
|
||||
|
||||
def _usermacro_map(self):
|
||||
""" use VM inventory maps """
|
||||
""" use VM usermacro maps """
|
||||
return vm_usermacro_map
|
||||
|
||||
def _tag_map(self):
|
||||
""" use VM tag maps """
|
||||
return vm_tag_map
|
||||
|
||||
def set_hostgroup(self, hg_format, nb_site_groups, nb_regions):
|
||||
"""Set the hostgroup for this device"""
|
||||
# Create new Hostgroup instance
|
||||
|
@ -173,6 +173,7 @@ def main(arguments):
|
||||
continue
|
||||
vm.set_inventory(nb_vm)
|
||||
vm.set_usermacros()
|
||||
vm.set_tags()
|
||||
# Checks if device is in cleanup state
|
||||
if vm.status in zabbix_device_removal:
|
||||
if vm.zabbix_id:
|
||||
@ -227,6 +228,7 @@ def main(arguments):
|
||||
continue
|
||||
device.set_inventory(nb_device)
|
||||
device.set_usermacros()
|
||||
device.set_tags()
|
||||
# Checks if device is part of cluster.
|
||||
# Requires clustering variable
|
||||
if device.isCluster() and clustering:
|
||||
|
Loading…
Reference in New Issue
Block a user