mirror of
https://github.com/TheNetworkGuy/netbox-zabbix-sync.git
synced 2026-03-21 20:18:38 -06:00
Changed sync function to class
This commit is contained in:
@@ -2,4 +2,4 @@
|
|||||||
Makes core module sync function available at package level for easier imports.
|
Makes core module sync function available at package level for easier imports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from netbox_zabbix_sync.modules.core import sync as sync
|
from netbox_zabbix_sync.modules.core import Sync as Sync
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import argparse
|
|||||||
import logging
|
import logging
|
||||||
from os import environ
|
from os import environ
|
||||||
|
|
||||||
from netbox_zabbix_sync.modules.core import sync
|
from netbox_zabbix_sync.modules.core import Sync
|
||||||
from netbox_zabbix_sync.modules.exceptions import EnvironmentVarError
|
from netbox_zabbix_sync.modules.exceptions import EnvironmentVarError
|
||||||
from netbox_zabbix_sync.modules.logging import get_logger, set_log_levels
|
from netbox_zabbix_sync.modules.logging import get_logger, set_log_levels, setup_logger
|
||||||
|
|
||||||
|
# Set logging
|
||||||
|
setup_logger()
|
||||||
logger = get_logger()
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +49,8 @@ def main(arguments):
|
|||||||
netbox_token = environ.get("NETBOX_TOKEN")
|
netbox_token = environ.get("NETBOX_TOKEN")
|
||||||
|
|
||||||
# Run main sync process
|
# Run main sync process
|
||||||
sync(
|
syncer = Sync()
|
||||||
|
syncer.connect(
|
||||||
nb_host=netbox_host,
|
nb_host=netbox_host,
|
||||||
nb_token=netbox_token,
|
nb_token=netbox_token,
|
||||||
zbx_host=zabbix_host,
|
zbx_host=zabbix_host,
|
||||||
@@ -55,6 +58,7 @@ def main(arguments):
|
|||||||
zbx_pass=zabbix_pass,
|
zbx_pass=zabbix_pass,
|
||||||
zbx_token=zabbix_token,
|
zbx_token=zabbix_token,
|
||||||
)
|
)
|
||||||
|
syncer.start()
|
||||||
|
|
||||||
|
|
||||||
def parse_cli():
|
def parse_cli():
|
||||||
|
|||||||
+314
-238
@@ -5,14 +5,13 @@ import sys
|
|||||||
from os import environ
|
from os import environ
|
||||||
|
|
||||||
from pynetbox import api
|
from pynetbox import api
|
||||||
from pynetbox.core.query import RequestError as NBRequestError
|
|
||||||
from requests.exceptions import ConnectionError as RequestsConnectionError
|
from requests.exceptions import ConnectionError as RequestsConnectionError
|
||||||
from zabbix_utils import APIRequestError, ProcessingError, ZabbixAPI
|
from zabbix_utils import APIRequestError, ProcessingError, ZabbixAPI
|
||||||
|
|
||||||
from netbox_zabbix_sync.modules.device import PhysicalDevice
|
from netbox_zabbix_sync.modules.device import PhysicalDevice
|
||||||
from netbox_zabbix_sync.modules.exceptions import SyncError
|
from netbox_zabbix_sync.modules.exceptions import SyncError
|
||||||
from netbox_zabbix_sync.modules.logging import get_logger, setup_logger
|
from netbox_zabbix_sync.modules.logging import get_logger
|
||||||
from netbox_zabbix_sync.modules.settings import load_config
|
from netbox_zabbix_sync.modules.settings import DEFAULT_CONFIG, load_config
|
||||||
from netbox_zabbix_sync.modules.tools import (
|
from netbox_zabbix_sync.modules.tools import (
|
||||||
convert_recordset,
|
convert_recordset,
|
||||||
proxy_prepper,
|
proxy_prepper,
|
||||||
@@ -20,259 +19,336 @@ from netbox_zabbix_sync.modules.tools import (
|
|||||||
)
|
)
|
||||||
from netbox_zabbix_sync.modules.virtual_machine import VirtualMachine
|
from netbox_zabbix_sync.modules.virtual_machine import VirtualMachine
|
||||||
|
|
||||||
# Import configuration settings
|
|
||||||
config = load_config()
|
|
||||||
|
|
||||||
|
|
||||||
setup_logger()
|
|
||||||
logger = get_logger()
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
def sync(nb_host, nb_token, zbx_host, zbx_user, zbx_pass, zbx_token):
|
class Sync:
|
||||||
"""
|
"""
|
||||||
Run the NetBox to Zabbix sync process.
|
Class that hosts the main sync process.
|
||||||
|
This class is used to connect to NetBox and Zabbix and run the sync process.
|
||||||
"""
|
"""
|
||||||
# Set NetBox API
|
|
||||||
netbox = api(nb_host, token=nb_token, threading=True)
|
def __init__(self, config=None):
|
||||||
# Create API call to get all custom fields which are on the device objects
|
"""
|
||||||
try:
|
Docstring for __init__
|
||||||
# Get NetBox version
|
|
||||||
nb_version = netbox.version
|
:param self: Description
|
||||||
logger.debug("NetBox version is %s.", nb_version)
|
:param config: Description
|
||||||
except RequestsConnectionError:
|
"""
|
||||||
logger.error(
|
self.netbox = None
|
||||||
"Unable to connect to NetBox with URL %s. Please check the URL and status of NetBox.",
|
self.zabbix = None
|
||||||
nb_host,
|
self.config = config
|
||||||
)
|
self.nb_version = None
|
||||||
sys.exit(1)
|
self._config()
|
||||||
except NBRequestError as e:
|
|
||||||
logger.error("NetBox error: %s", e)
|
def _config(self):
|
||||||
sys.exit(1)
|
"""
|
||||||
# Check if the provided Hostgroup layout is valid
|
Load config and check if provided config is valid.
|
||||||
device_cfs = []
|
"""
|
||||||
vm_cfs = []
|
if not self.config:
|
||||||
device_cfs = list(
|
self.config = load_config()
|
||||||
netbox.extras.custom_fields.filter(
|
return True
|
||||||
type=["text", "object", "select"], content_types="dcim.device"
|
# Check if provided config is valid
|
||||||
)
|
if not isinstance(self.config, dict):
|
||||||
)
|
e = "Provided config is not a dictionary."
|
||||||
verify_hg_format(
|
logger.error(e)
|
||||||
config["hostgroup_format"], device_cfs=device_cfs, hg_type="dev", logger=logger
|
raise SyncError(e)
|
||||||
)
|
# Combine default options and provided config
|
||||||
if config["sync_vms"]:
|
default_config = DEFAULT_CONFIG.copy()
|
||||||
vm_cfs = list(
|
for key in self.config:
|
||||||
netbox.extras.custom_fields.filter(
|
# Check if the user provided an invalid option parameter
|
||||||
type=["text", "object", "select"],
|
if key not in default_config:
|
||||||
content_types="virtualization.virtualmachine",
|
e = f"Provided config contains invalid key: {key}."
|
||||||
|
logger.error(e)
|
||||||
|
raise SyncError(e)
|
||||||
|
# Remove keys from default config to keep track of missing keys
|
||||||
|
default_config.pop(key)
|
||||||
|
# Add missing options with default values
|
||||||
|
for key in default_config:
|
||||||
|
self.config[key] = default_config[key]
|
||||||
|
return True
|
||||||
|
|
||||||
|
def connect(
|
||||||
|
self, nb_host, nb_token, zbx_host, zbx_user=None, zbx_pass=None, zbx_token=None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Docstring for connect
|
||||||
|
|
||||||
|
:param self: Description
|
||||||
|
:param nb_host: Description
|
||||||
|
:param nb_token: Description
|
||||||
|
:param zbx_host: Description
|
||||||
|
:param zbx_user: Description
|
||||||
|
:param zbx_pass: Description
|
||||||
|
:param zbx_token: Description
|
||||||
|
"""
|
||||||
|
# Initialize Netbox API connection
|
||||||
|
netbox = api(nb_host, token=nb_token, threading=True)
|
||||||
|
try:
|
||||||
|
# Get NetBox version
|
||||||
|
nb_version = netbox.version
|
||||||
|
logger.debug("NetBox version is %s.", nb_version)
|
||||||
|
self.netbox = netbox
|
||||||
|
self.nb_version = nb_version
|
||||||
|
except RequestsConnectionError:
|
||||||
|
logger.error(
|
||||||
|
"Unable to connect to NetBox with URL %s. Please check the URL and status of NetBox.",
|
||||||
|
nb_host,
|
||||||
|
)
|
||||||
|
# Set Zabbix API
|
||||||
|
try:
|
||||||
|
ssl_ctx = ssl.create_default_context()
|
||||||
|
|
||||||
|
# If a custom CA bundle is set for pynetbox (requests), also use it for the Zabbix API
|
||||||
|
if environ.get("REQUESTS_CA_BUNDLE", None):
|
||||||
|
ssl_ctx.load_verify_locations(environ["REQUESTS_CA_BUNDLE"])
|
||||||
|
|
||||||
|
if not zbx_token:
|
||||||
|
self.zabbix = ZabbixAPI(
|
||||||
|
zbx_host, user=zbx_user, password=zbx_pass, ssl_context=ssl_ctx
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.zabbix = ZabbixAPI(zbx_host, token=zbx_token, ssl_context=ssl_ctx)
|
||||||
|
self.zabbix.check_auth()
|
||||||
|
except (APIRequestError, ProcessingError) as zbx_error:
|
||||||
|
e = f"Zabbix returned the following error: {zbx_error}."
|
||||||
|
logger.error(e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Run the NetBox to Zabbix sync process.
|
||||||
|
"""
|
||||||
|
if not self.netbox or not self.zabbix:
|
||||||
|
e = "Not connected to NetBox or Zabbix. Please run the connect function first."
|
||||||
|
logger.error(e)
|
||||||
|
raise SyncError(e)
|
||||||
|
device_cfs = []
|
||||||
|
vm_cfs = []
|
||||||
|
# Create API call to get all custom fields which are on the device objects
|
||||||
|
device_cfs = list(
|
||||||
|
self.netbox.extras.custom_fields.filter(
|
||||||
|
type=["text", "object", "select"], content_types="dcim.device"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# Check if the provided Hostgroup layout is valid
|
||||||
verify_hg_format(
|
verify_hg_format(
|
||||||
config["vm_hostgroup_format"], vm_cfs=vm_cfs, hg_type="vm", logger=logger
|
self.config["hostgroup_format"],
|
||||||
|
device_cfs=device_cfs,
|
||||||
|
hg_type="dev",
|
||||||
|
logger=logger,
|
||||||
)
|
)
|
||||||
# Set Zabbix API
|
if self.config["sync_vms"]:
|
||||||
try:
|
vm_cfs = list(
|
||||||
ssl_ctx = ssl.create_default_context()
|
self.netbox.extras.custom_fields.filter(
|
||||||
|
type=["text", "object", "select"],
|
||||||
# If a custom CA bundle is set for pynetbox (requests), also use it for the Zabbix API
|
content_types="virtualization.virtualmachine",
|
||||||
if environ.get("REQUESTS_CA_BUNDLE", None):
|
)
|
||||||
ssl_ctx.load_verify_locations(environ["REQUESTS_CA_BUNDLE"])
|
|
||||||
|
|
||||||
if not zbx_token:
|
|
||||||
zabbix = ZabbixAPI(
|
|
||||||
zbx_host, user=zbx_user, password=zbx_pass, ssl_context=ssl_ctx
|
|
||||||
)
|
)
|
||||||
else:
|
verify_hg_format(
|
||||||
zabbix = ZabbixAPI(zbx_host, token=zbx_token, ssl_context=ssl_ctx)
|
self.config["vm_hostgroup_format"],
|
||||||
zabbix.check_auth()
|
vm_cfs=vm_cfs,
|
||||||
except (APIRequestError, ProcessingError) as zbx_error:
|
hg_type="vm",
|
||||||
e = f"Zabbix returned the following error: {zbx_error}."
|
logger=logger,
|
||||||
logger.error(e)
|
)
|
||||||
sys.exit(1)
|
# Set API parameter mapping based on API version
|
||||||
# Set API parameter mapping based on API version
|
proxy_name = "host" if not str(self.zabbix.version).startswith("7") else "name"
|
||||||
proxy_name = "host" if not str(zabbix.version).startswith("7") else "name"
|
# Get all Zabbix and NetBox data
|
||||||
# Get all Zabbix and NetBox data
|
netbox_devices = list(
|
||||||
netbox_devices = list(netbox.dcim.devices.filter(**config["nb_device_filter"]))
|
self.netbox.dcim.devices.filter(**self.config["nb_device_filter"])
|
||||||
netbox_vms = []
|
|
||||||
if config["sync_vms"]:
|
|
||||||
netbox_vms = list(
|
|
||||||
netbox.virtualization.virtual_machines.filter(**config["nb_vm_filter"])
|
|
||||||
)
|
)
|
||||||
netbox_site_groups = convert_recordset(netbox.dcim.site_groups.all())
|
netbox_vms = []
|
||||||
netbox_regions = convert_recordset(netbox.dcim.regions.all())
|
if self.config["sync_vms"]:
|
||||||
netbox_journals = netbox.extras.journal_entries
|
netbox_vms = list(
|
||||||
zabbix_groups = zabbix.hostgroup.get(output=["groupid", "name"]) # type: ignore[attr-defined]
|
self.netbox.virtualization.virtual_machines.filter(
|
||||||
zabbix_templates = zabbix.template.get(output=["templateid", "name"]) # type: ignore[attr-defined]
|
**self.config["nb_vm_filter"]
|
||||||
zabbix_proxies = zabbix.proxy.get(output=["proxyid", proxy_name]) # type: ignore[attr-defined]
|
)
|
||||||
# Set empty list for proxy processing Zabbix <= 6
|
)
|
||||||
zabbix_proxygroups = []
|
netbox_site_groups = convert_recordset(self.netbox.dcim.site_groups.all())
|
||||||
if str(zabbix.version).startswith("7"):
|
netbox_regions = convert_recordset(self.netbox.dcim.regions.all())
|
||||||
zabbix_proxygroups = zabbix.proxygroup.get(output=["proxy_groupid", "name"]) # type: ignore[attr-defined]
|
netbox_journals = self.netbox.extras.journal_entries
|
||||||
# Sanitize proxy data
|
zabbix_groups = self.zabbix.hostgroup.get(output=["groupid", "name"]) # type: ignore[attr-defined]
|
||||||
if proxy_name == "host":
|
zabbix_templates = self.zabbix.template.get(output=["templateid", "name"]) # type: ignore[attr-defined]
|
||||||
for proxy in zabbix_proxies:
|
zabbix_proxies = self.zabbix.proxy.get(output=["proxyid", proxy_name]) # type: ignore[attr-defined]
|
||||||
proxy["name"] = proxy.pop("host")
|
# Set empty list for proxy processing Zabbix <= 6
|
||||||
# Prepare list of all proxy and proxy_groups
|
zabbix_proxygroups = []
|
||||||
zabbix_proxy_list = proxy_prepper(zabbix_proxies, zabbix_proxygroups)
|
if str(self.zabbix.version).startswith("7"):
|
||||||
|
zabbix_proxygroups = self.zabbix.proxygroup.get(
|
||||||
|
output=["proxy_groupid", "name"]
|
||||||
|
) # type: ignore[attr-defined]
|
||||||
|
# Sanitize proxy data
|
||||||
|
if proxy_name == "host":
|
||||||
|
for proxy in zabbix_proxies:
|
||||||
|
proxy["name"] = proxy.pop("host")
|
||||||
|
# Prepare list of all proxy and proxy_groups
|
||||||
|
zabbix_proxy_list = proxy_prepper(zabbix_proxies, zabbix_proxygroups)
|
||||||
|
|
||||||
# Go through all NetBox devices
|
# Go through all NetBox devices
|
||||||
for nb_vm in netbox_vms:
|
for nb_vm in netbox_vms:
|
||||||
try:
|
try:
|
||||||
vm = VirtualMachine(
|
vm = VirtualMachine(
|
||||||
nb_vm,
|
nb_vm,
|
||||||
zabbix,
|
self.zabbix,
|
||||||
netbox_journals,
|
netbox_journals,
|
||||||
nb_version,
|
self.nb_version,
|
||||||
config["create_journal"],
|
self.config["create_journal"],
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
logger.debug("Host %s: Started operations on VM.", vm.name)
|
logger.debug("Host %s: Started operations on VM.", vm.name)
|
||||||
vm.set_vm_template()
|
vm.set_vm_template()
|
||||||
# Check if a valid template has been found for this VM.
|
# Check if a valid template has been found for this VM.
|
||||||
if not vm.zbx_template_names:
|
if not vm.zbx_template_names:
|
||||||
continue
|
|
||||||
vm.set_hostgroup(
|
|
||||||
config["vm_hostgroup_format"], netbox_site_groups, netbox_regions
|
|
||||||
)
|
|
||||||
# Check if a valid hostgroup has been found for this VM.
|
|
||||||
if not vm.hostgroups:
|
|
||||||
continue
|
|
||||||
if config["extended_site_properties"] and nb_vm.site:
|
|
||||||
logger.debug("VM %s: extending site information.", vm.name)
|
|
||||||
vm.site = convert_recordset(netbox.dcim.sites.filter(id=nb_vm.site.id)) # type: ignore[attr-defined]
|
|
||||||
vm.set_inventory(nb_vm)
|
|
||||||
vm.set_usermacros()
|
|
||||||
vm.set_tags()
|
|
||||||
# Checks if device is in cleanup state
|
|
||||||
if vm.status in config["zabbix_device_removal"]:
|
|
||||||
if vm.zabbix_id:
|
|
||||||
# Delete device from Zabbix
|
|
||||||
# and remove hostID from NetBox.
|
|
||||||
vm.cleanup()
|
|
||||||
logger.info("VM %s: cleanup complete", vm.name)
|
|
||||||
continue
|
continue
|
||||||
# Device has been added to NetBox
|
vm.set_hostgroup(
|
||||||
# but is not in Activate state
|
self.config["vm_hostgroup_format"],
|
||||||
logger.info(
|
netbox_site_groups,
|
||||||
"VM %s: Skipping since this VM is not in the active state.", vm.name
|
netbox_regions,
|
||||||
)
|
)
|
||||||
continue
|
# Check if a valid hostgroup has been found for this VM.
|
||||||
# Check if the VM is in the disabled state
|
if not vm.hostgroups:
|
||||||
if vm.status in config["zabbix_device_disable"]:
|
continue
|
||||||
vm.zabbix_state = 1
|
if self.config["extended_site_properties"] and nb_vm.site:
|
||||||
# Add hostgroup if config is set
|
logger.debug("VM %s: extending site information.", vm.name)
|
||||||
if config["create_hostgroups"]:
|
vm.site = convert_recordset(
|
||||||
# Create new hostgroup. Potentially multiple groups if nested
|
self.netbox.dcim.sites.filter(id=nb_vm.site.id)
|
||||||
hostgroups = vm.create_zbx_hostgroup(zabbix_groups)
|
) # type: ignore[attr-defined]
|
||||||
# go through all newly created hostgroups
|
vm.set_inventory(nb_vm)
|
||||||
for group in hostgroups:
|
vm.set_usermacros()
|
||||||
# Add new hostgroups to zabbix group list
|
vm.set_tags()
|
||||||
zabbix_groups.append(group)
|
# Checks if device is in cleanup state
|
||||||
# Check if VM is already in Zabbix
|
if vm.status in self.config["zabbix_device_removal"]:
|
||||||
if vm.zabbix_id:
|
if vm.zabbix_id:
|
||||||
vm.consistency_check(
|
# Delete device from Zabbix
|
||||||
zabbix_groups,
|
# and remove hostID from self.netbox.
|
||||||
zabbix_templates,
|
vm.cleanup()
|
||||||
zabbix_proxy_list,
|
logger.info("VM %s: cleanup complete", vm.name)
|
||||||
config["full_proxy_sync"],
|
continue
|
||||||
config["create_hostgroups"],
|
# Device has been added to NetBox
|
||||||
)
|
# but is not in Activate state
|
||||||
continue
|
|
||||||
# Add VM to Zabbix
|
|
||||||
vm.create_in_zabbix(zabbix_groups, zabbix_templates, zabbix_proxy_list)
|
|
||||||
except SyncError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for nb_device in netbox_devices:
|
|
||||||
try:
|
|
||||||
# Set device instance set data such as hostgroup and template information.
|
|
||||||
device = PhysicalDevice(
|
|
||||||
nb_device,
|
|
||||||
zabbix,
|
|
||||||
netbox_journals,
|
|
||||||
nb_version,
|
|
||||||
config["create_journal"],
|
|
||||||
logger,
|
|
||||||
)
|
|
||||||
logger.debug("Host %s: Started operations on device.", device.name)
|
|
||||||
device.set_template(
|
|
||||||
config["templates_config_context"],
|
|
||||||
config["templates_config_context_overrule"],
|
|
||||||
)
|
|
||||||
# Check if a valid template has been found for this VM.
|
|
||||||
if not device.zbx_template_names:
|
|
||||||
continue
|
|
||||||
device.set_hostgroup(
|
|
||||||
config["hostgroup_format"], netbox_site_groups, netbox_regions
|
|
||||||
)
|
|
||||||
# Check if a valid hostgroup has been found for this VM.
|
|
||||||
if not device.hostgroups:
|
|
||||||
logger.warning(
|
|
||||||
"Host %s: Host has no valid hostgroups, Skipping this host...",
|
|
||||||
device.name,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
if config["extended_site_properties"] and nb_device.site:
|
|
||||||
logger.debug("Device %s: extending site information.", device.name)
|
|
||||||
device.site = convert_recordset( # type: ignore[attr-defined]
|
|
||||||
netbox.dcim.sites.filter(id=nb_device.site.id)
|
|
||||||
)
|
|
||||||
device.set_inventory(nb_device)
|
|
||||||
device.set_usermacros()
|
|
||||||
device.set_tags()
|
|
||||||
# Checks if device is part of cluster.
|
|
||||||
# Requires clustering variable
|
|
||||||
if device.is_cluster() and config["clustering"]:
|
|
||||||
# Check if device is primary or secondary
|
|
||||||
if device.promote_primary_device():
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Device %s: is part of cluster and primary.", device.name
|
"VM %s: Skipping since this VM is not in the active state.",
|
||||||
|
vm.name,
|
||||||
)
|
)
|
||||||
else:
|
continue
|
||||||
# Device is secondary in cluster.
|
# Check if the VM is in the disabled state
|
||||||
# Don't continue with this device.
|
if vm.status in self.config["zabbix_device_disable"]:
|
||||||
logger.info(
|
vm.zabbix_state = 1
|
||||||
"Device %s: Is part of cluster but not primary. Skipping this host...",
|
# Add hostgroup if config is set
|
||||||
|
if self.config["create_hostgroups"]:
|
||||||
|
# Create new hostgroup. Potentially multiple groups if nested
|
||||||
|
hostgroups = vm.create_zbx_hostgroup(zabbix_groups)
|
||||||
|
# go through all newly created hostgroups
|
||||||
|
for group in hostgroups:
|
||||||
|
# Add new hostgroups to zabbix group list
|
||||||
|
zabbix_groups.append(group)
|
||||||
|
# Check if VM is already in Zabbix
|
||||||
|
if vm.zabbix_id:
|
||||||
|
vm.consistency_check(
|
||||||
|
zabbix_groups,
|
||||||
|
zabbix_templates,
|
||||||
|
zabbix_proxy_list,
|
||||||
|
self.config["full_proxy_sync"],
|
||||||
|
self.config["create_hostgroups"],
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
# Add VM to Zabbix
|
||||||
|
vm.create_in_zabbix(zabbix_groups, zabbix_templates, zabbix_proxy_list)
|
||||||
|
except SyncError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for nb_device in netbox_devices:
|
||||||
|
try:
|
||||||
|
# Set device instance set data such as hostgroup and template information.
|
||||||
|
device = PhysicalDevice(
|
||||||
|
nb_device,
|
||||||
|
self.zabbix,
|
||||||
|
netbox_journals,
|
||||||
|
self.nb_version,
|
||||||
|
self.config["create_journal"],
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
logger.debug("Host %s: Started operations on device.", device.name)
|
||||||
|
device.set_template(
|
||||||
|
self.config["templates_config_context"],
|
||||||
|
self.config["templates_config_context_overrule"],
|
||||||
|
)
|
||||||
|
# Check if a valid template has been found for this VM.
|
||||||
|
if not device.zbx_template_names:
|
||||||
|
continue
|
||||||
|
device.set_hostgroup(
|
||||||
|
self.config["hostgroup_format"], netbox_site_groups, netbox_regions
|
||||||
|
)
|
||||||
|
# Check if a valid hostgroup has been found for this VM.
|
||||||
|
if not device.hostgroups:
|
||||||
|
logger.warning(
|
||||||
|
"Host %s: Host has no valid hostgroups, Skipping this host...",
|
||||||
device.name,
|
device.name,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
# Checks if device is in cleanup state
|
if self.config["extended_site_properties"] and nb_device.site:
|
||||||
if device.status in config["zabbix_device_removal"]:
|
logger.debug("Device %s: extending site information.", device.name)
|
||||||
if device.zabbix_id:
|
device.site = convert_recordset( # type: ignore[attr-defined]
|
||||||
# Delete device from Zabbix
|
self.netbox.dcim.sites.filter(id=nb_device.site.id)
|
||||||
# and remove hostID from NetBox.
|
)
|
||||||
device.cleanup()
|
device.set_inventory(nb_device)
|
||||||
logger.info("Device %s: cleanup complete", device.name)
|
device.set_usermacros()
|
||||||
|
device.set_tags()
|
||||||
|
# Checks if device is part of cluster.
|
||||||
|
# Requires clustering variable
|
||||||
|
if device.is_cluster() and self.config["clustering"]:
|
||||||
|
# Check if device is primary or secondary
|
||||||
|
if device.promote_primary_device():
|
||||||
|
logger.info(
|
||||||
|
"Device %s: is part of cluster and primary.", device.name
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Device is secondary in cluster.
|
||||||
|
# Don't continue with this device.
|
||||||
|
logger.info(
|
||||||
|
"Device %s: Is part of cluster but not primary. Skipping this host...",
|
||||||
|
device.name,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
# Checks if device is in cleanup state
|
||||||
|
if device.status in self.config["zabbix_device_removal"]:
|
||||||
|
if device.zabbix_id:
|
||||||
|
# Delete device from Zabbix
|
||||||
|
# and remove hostID from NetBox.
|
||||||
|
device.cleanup()
|
||||||
|
logger.info("Device %s: cleanup complete", device.name)
|
||||||
|
continue
|
||||||
|
# Device has been added to NetBox
|
||||||
|
# but is not in Activate state
|
||||||
|
logger.info(
|
||||||
|
"Device %s: Skipping since this device is not in the active state.",
|
||||||
|
device.name,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
# Device has been added to NetBox
|
# Check if the device is in the disabled state
|
||||||
# but is not in Activate state
|
if device.status in self.config["zabbix_device_disable"]:
|
||||||
logger.info(
|
device.zabbix_state = 1
|
||||||
"Device %s: Skipping since this device is not in the active state.",
|
# Add hostgroup is config is set
|
||||||
device.name,
|
if self.config["create_hostgroups"]:
|
||||||
|
# Create new hostgroup. Potentially multiple groups if nested
|
||||||
|
hostgroups = device.create_zbx_hostgroup(zabbix_groups)
|
||||||
|
# go through all newly created hostgroups
|
||||||
|
for group in hostgroups:
|
||||||
|
# Add new hostgroups to zabbix group list
|
||||||
|
zabbix_groups.append(group)
|
||||||
|
# Check if device is already in Zabbix
|
||||||
|
if device.zabbix_id:
|
||||||
|
device.consistency_check(
|
||||||
|
zabbix_groups,
|
||||||
|
zabbix_templates,
|
||||||
|
zabbix_proxy_list,
|
||||||
|
self.config["full_proxy_sync"],
|
||||||
|
self.config["create_hostgroups"],
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
# Add device to Zabbix
|
||||||
|
device.create_in_zabbix(
|
||||||
|
zabbix_groups, zabbix_templates, zabbix_proxy_list
|
||||||
)
|
)
|
||||||
continue
|
except SyncError:
|
||||||
# Check if the device is in the disabled state
|
pass
|
||||||
if device.status in config["zabbix_device_disable"]:
|
self.zabbix.logout()
|
||||||
device.zabbix_state = 1
|
|
||||||
# Add hostgroup is config is set
|
|
||||||
if config["create_hostgroups"]:
|
|
||||||
# Create new hostgroup. Potentially multiple groups if nested
|
|
||||||
hostgroups = device.create_zbx_hostgroup(zabbix_groups)
|
|
||||||
# go through all newly created hostgroups
|
|
||||||
for group in hostgroups:
|
|
||||||
# Add new hostgroups to zabbix group list
|
|
||||||
zabbix_groups.append(group)
|
|
||||||
# Check if device is already in Zabbix
|
|
||||||
if device.zabbix_id:
|
|
||||||
device.consistency_check(
|
|
||||||
zabbix_groups,
|
|
||||||
zabbix_templates,
|
|
||||||
zabbix_proxy_list,
|
|
||||||
config["full_proxy_sync"],
|
|
||||||
config["create_hostgroups"],
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
# Add device to Zabbix
|
|
||||||
device.create_in_zabbix(zabbix_groups, zabbix_templates, zabbix_proxy_list)
|
|
||||||
except SyncError:
|
|
||||||
pass
|
|
||||||
zabbix.logout()
|
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ select = [
|
|||||||
"tests/*" = [
|
"tests/*" = [
|
||||||
# Ignore use of assert
|
# Ignore use of assert
|
||||||
"S101",
|
"S101",
|
||||||
|
# Ignore hardcoded passwords / tokens
|
||||||
|
"S106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+30
-25
@@ -7,7 +7,7 @@ from pynetbox.core.query import RequestError as NBRequestError
|
|||||||
from requests.exceptions import ConnectionError as RequestsConnectionError
|
from requests.exceptions import ConnectionError as RequestsConnectionError
|
||||||
from zabbix_utils import APIRequestError, ProcessingError
|
from zabbix_utils import APIRequestError, ProcessingError
|
||||||
|
|
||||||
from netbox_zabbix_sync.modules.core import sync
|
from netbox_zabbix_sync.modules.core import Sync
|
||||||
|
|
||||||
# Minimal config for testing - includes all keys used by sync()
|
# Minimal config for testing - includes all keys used by sync()
|
||||||
TEST_CONFIG = {
|
TEST_CONFIG = {
|
||||||
@@ -88,14 +88,16 @@ class TestSyncNetboxConnection(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as context:
|
with self.assertRaises(SystemExit) as context:
|
||||||
sync(
|
syncer = Sync()
|
||||||
"http://netbox.local",
|
syncer.connect(
|
||||||
"token",
|
nb_host="http://netbox.local",
|
||||||
"http://zabbix.local",
|
nb_token="token",
|
||||||
"user",
|
zbx_host="http://zabbix.local",
|
||||||
"pass",
|
zbx_user="user",
|
||||||
None,
|
zbx_pass="pass",
|
||||||
|
zbx_token=None,
|
||||||
)
|
)
|
||||||
|
syncer.start()
|
||||||
|
|
||||||
self.assertEqual(context.exception.code, 1)
|
self.assertEqual(context.exception.code, 1)
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ class TestSyncNetboxConnection(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as context:
|
with self.assertRaises(SystemExit) as context:
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"token",
|
"token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -150,7 +152,7 @@ class TestSyncZabbixConnection(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as context:
|
with self.assertRaises(SystemExit) as context:
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"token",
|
"token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -173,7 +175,7 @@ class TestSyncZabbixConnection(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with self.assertRaises(SystemExit) as context:
|
with self.assertRaises(SystemExit) as context:
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"token",
|
"token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -219,7 +221,7 @@ class TestSyncZabbixAuthentication(unittest.TestCase):
|
|||||||
self._setup_netbox_mock(mock_api)
|
self._setup_netbox_mock(mock_api)
|
||||||
self._setup_zabbix_mock(mock_zabbix_api)
|
self._setup_zabbix_mock(mock_zabbix_api)
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -243,7 +245,7 @@ class TestSyncZabbixAuthentication(unittest.TestCase):
|
|||||||
self._setup_netbox_mock(mock_api)
|
self._setup_netbox_mock(mock_api)
|
||||||
self._setup_zabbix_mock(mock_zabbix_api)
|
self._setup_zabbix_mock(mock_zabbix_api)
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -281,7 +283,8 @@ class TestSyncDeviceProcessing(unittest.TestCase):
|
|||||||
mock_zabbix = MagicMock()
|
mock_zabbix = MagicMock()
|
||||||
mock_zabbix_api.return_value = mock_zabbix
|
mock_zabbix_api.return_value = mock_zabbix
|
||||||
mock_zabbix.version = version
|
mock_zabbix.version = version
|
||||||
mock_zabbix.hostgroup.get.return_value = [{"groupid": "1", "name": "TestGroup"}]
|
mock_zabbix.hostgroup.get.return_value = [
|
||||||
|
{"groupid": "1", "name": "TestGroup"}]
|
||||||
mock_zabbix.template.get.return_value = [
|
mock_zabbix.template.get.return_value = [
|
||||||
{"templateid": "1", "name": "TestTemplate"}
|
{"templateid": "1", "name": "TestTemplate"}
|
||||||
]
|
]
|
||||||
@@ -308,7 +311,7 @@ class TestSyncDeviceProcessing(unittest.TestCase):
|
|||||||
mock_device_instance.zbx_template_names = []
|
mock_device_instance.zbx_template_names = []
|
||||||
mock_physical_device.return_value = mock_device_instance
|
mock_physical_device.return_value = mock_device_instance
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -340,7 +343,7 @@ class TestSyncDeviceProcessing(unittest.TestCase):
|
|||||||
mock_vm_instance.zbx_template_names = []
|
mock_vm_instance.zbx_template_names = []
|
||||||
mock_virtual_machine.return_value = mock_vm_instance
|
mock_virtual_machine.return_value = mock_vm_instance
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -365,7 +368,7 @@ class TestSyncDeviceProcessing(unittest.TestCase):
|
|||||||
self._setup_netbox_mock(mock_api, vms=[vm1])
|
self._setup_netbox_mock(mock_api, vms=[vm1])
|
||||||
self._setup_zabbix_mock(mock_zabbix_api)
|
self._setup_zabbix_mock(mock_zabbix_api)
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -405,9 +408,10 @@ class TestSyncZabbixVersionHandling(unittest.TestCase):
|
|||||||
mock_zabbix.version = "6.0"
|
mock_zabbix.version = "6.0"
|
||||||
mock_zabbix.hostgroup.get.return_value = []
|
mock_zabbix.hostgroup.get.return_value = []
|
||||||
mock_zabbix.template.get.return_value = []
|
mock_zabbix.template.get.return_value = []
|
||||||
mock_zabbix.proxy.get.return_value = [{"proxyid": "1", "host": "proxy1"}]
|
mock_zabbix.proxy.get.return_value = [
|
||||||
|
{"proxyid": "1", "host": "proxy1"}]
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -431,10 +435,11 @@ class TestSyncZabbixVersionHandling(unittest.TestCase):
|
|||||||
mock_zabbix.version = "7.0"
|
mock_zabbix.version = "7.0"
|
||||||
mock_zabbix.hostgroup.get.return_value = []
|
mock_zabbix.hostgroup.get.return_value = []
|
||||||
mock_zabbix.template.get.return_value = []
|
mock_zabbix.template.get.return_value = []
|
||||||
mock_zabbix.proxy.get.return_value = [{"proxyid": "1", "name": "proxy1"}]
|
mock_zabbix.proxy.get.return_value = [
|
||||||
|
{"proxyid": "1", "name": "proxy1"}]
|
||||||
mock_zabbix.proxygroup.get.return_value = []
|
mock_zabbix.proxygroup.get.return_value = []
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -461,7 +466,7 @@ class TestSyncZabbixVersionHandling(unittest.TestCase):
|
|||||||
mock_zabbix.proxy.get.return_value = []
|
mock_zabbix.proxy.get.return_value = []
|
||||||
mock_zabbix.proxygroup.get.return_value = []
|
mock_zabbix.proxygroup.get.return_value = []
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -487,7 +492,7 @@ class TestSyncZabbixVersionHandling(unittest.TestCase):
|
|||||||
mock_zabbix.template.get.return_value = []
|
mock_zabbix.template.get.return_value = []
|
||||||
mock_zabbix.proxy.get.return_value = []
|
mock_zabbix.proxy.get.return_value = []
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -529,7 +534,7 @@ class TestSyncLogout(unittest.TestCase):
|
|||||||
mock_zabbix.template.get.return_value = []
|
mock_zabbix.template.get.return_value = []
|
||||||
mock_zabbix.proxy.get.return_value = []
|
mock_zabbix.proxy.get.return_value = []
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
@@ -579,7 +584,7 @@ class TestSyncProxyNameSanitization(unittest.TestCase):
|
|||||||
]
|
]
|
||||||
mock_proxy_prepper.return_value = []
|
mock_proxy_prepper.return_value = []
|
||||||
|
|
||||||
sync(
|
Sync(
|
||||||
"http://netbox.local",
|
"http://netbox.local",
|
||||||
"nb_token",
|
"nb_token",
|
||||||
"http://zabbix.local",
|
"http://zabbix.local",
|
||||||
|
|||||||
Reference in New Issue
Block a user